[
  {
    "path": ".dockerignore",
    "content": ".idea/\nvendor/"
  },
  {
    "path": ".gitbook.yaml",
    "content": "root: ./docs/"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nThe GitHub issue tracker is for bug reports and feature requests.\nGeneral support can be found at the following locations:\n\n- Cayley Forum - https://discourse.cayley.io\n- Slack - cayleygraph.slack.com, invites: https://cayley-slackin.herokuapp.com\n\nPlease answer these questions before submitting your issue. Thanks!\n-->\n\n**Description**\n\n<!--\nBriefly describe the problem you are having in a few paragraphs.\n-->\n\n**Steps to reproduce the issue:**\n1.\n2.\n3.\n\n**Received results:**\n\n<!--\nDescribe results you received.\n\nAny additional information such as logs will help.\nEnclose it into following markdown:\n-->\n```\n(program output or server response)\n```\n\n**Expected results:**\n\n\n**Output of `cayley version` or commit hash:**\n\n```\n(paste your output here)\n```\n\n**Environment details:**\n\nBackend database: `(database and version)`\n"
  },
  {
    "path": ".github/workflows/build-and-release.yml",
    "content": "name: Test and release\n\non:\n  push:\n    branches:\n      - master\n      - ci_tests\n    tags:\n      - '*'\n  pull_request:\n    branches:\n      - master\n\nenv:\n  DOCKER_IMAGE_NAME: ghcr.io/cayleygraph/cayley\n\njobs:\n\n  tests:\n    name: Tests\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Set up Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: '1.22'\n\n    - name: Dependencies\n      run: go mod download\n\n    - name: Vet\n      run: ./vet.sh\n\n    - name: Build\n      run: go build -v ./cmd/cayley\n\n    - name: Test\n      run: go test -v ./...\n\n  release:\n    name: Release\n    runs-on: ubuntu-latest\n    if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/ci_tests')\n    needs:\n      - tests\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: '1.22'\n\n      - name: Dependencies\n        run: go mod download\n\n      - name: Download UI\n        run: go run cmd/download_ui/download_ui.go\n\n      - name: Release\n        uses: goreleaser/goreleaser-action@v6\n        if: startsWith(github.ref, 'refs/tags/v')\n        with:\n          version: latest\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n  docker:\n    name: Docker image\n    runs-on: ubuntu-latest\n    if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/ci_tests')\n    needs:\n      - tests\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Docker build\n        run: |\n          docker build -t $DOCKER_IMAGE_NAME:dev --build-arg VERSION=${{ github.ref_name }} .\n\n      - name: Log in to the Container registry\n        uses: docker/login-action@v3\n        if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/master')\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Push latest\n        if: (github.ref == 'refs/heads/master')\n        run: |\n          docker push $DOCKER_IMAGE_NAME:dev\n\n      - name: Push tagged\n        if: startsWith(github.ref, 'refs/tags/')\n        run: |\n          docker tag $DOCKER_IMAGE_NAME:dev $DOCKER_IMAGE_NAME:${{ github.ref_name }}\n          docker push $DOCKER_IMAGE_NAME:${{ github.ref_name }}\n\n      - name: Push latest\n        if: startsWith(github.ref, 'refs/tags/v')\n        run: |\n          docker tag $DOCKER_IMAGE_NAME:dev $DOCKER_IMAGE_NAME:latest\n          docker push $DOCKER_IMAGE_NAME:latest"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\ndb/\ndist/\nvendor/\n\n*.swp\nmain\n*.test\n*.peg.go\ncayley.cfg\ncayley.yml\ncayley.json\n.cayley_history\n.DS_Store\n\npackrd/packed-*\n# The build binary\n/cayley\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "builds:\n  - main: ./cmd/cayley\n    binary: cayley\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - darwin\n      - windows\n    goarch:\n      - amd64\n      - arm64\n      - arm\n    ignore:\n      - goos: windows\n        goarch: arm\n      - goos: windows\n        goarch: arm64\n      - goos: darwin\n        goarch: arm\n      - goos: darwin\n        goarch: arm64\n    ldflags:\n      - -s -w -X github.com/cayleygraph/cayley/version.Version={{.Version}} -X github.com/cayleygraph/cayley/version.GitHash={{.FullCommit}} -X github.com/cayleygraph/cayley/version.BuildDate={{.Date}}\narchives:\n  - format: tar.gz\n    format_overrides:\n      - goos: windows\n        format: zip\n    wrap_in_directory: true\n    files:\n      - README.md\n      - LICENSE\n      - AUTHORS\n      - CONTRIBUTORS\n      - configurations/*\n      - data/*\nchecksum:\n  name_template: \"checksums.txt\"\nsnapshot:\n  name_template: \"{{ .Tag }}-dev\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n      - \"^docs:\"\n      - \"^test:\"\n      - \"^ci:\"\nsnapcrafts:\n  - name: cayley\n    publish: false # TODO(dennwc): enable build when the package is reviewed\n    summary: Open-source graph inspired by Freebase and Google's Knowledge Graph.\n    description: |\n      Cayley is an open-source graph inspired by the graph database behind Freebase and Google's Knowledge Graph.\n      Its goal is to be a part of the developer's toolbox where Linked Data and graph-shaped data\n      (semantic webs, social networks, etc) in general are concerned.\n    grade: stable\n    confinement: strict\n    license: Apache-2.0\n    base: core18\n    apps:\n      cayley:\n        plugs: [\"home\", \"network\", \"network-bind\", \"personal-files\"]\n    plugs:\n      personal-files:\n        read:\n          - $HOME/.cayley\n        write:\n          - $HOME/.cayley\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the official list of Cayley authors for copyright purposes.\n# This file is distinct from the CONTRIBUTORS files.\n# See the latter for an explanation.\n#\n# Names should be added to this file as:\n# Name or Organization <email address>\n# The email address is not required for organizations.\n#\n# Please keep the list sorted.\n\nAlexander Peters <info@alexanderpeters.de>\nAndrew Dunham <andrew@du.nham.ca>\nBram Leenders <bcleenders@gmail.com>\nBrendan Ball <ball.brendan50@gmail.com>\nDenys Smirnov <dennwc@pm.me>\nDerek Liang <fr.derekliang@gmail.com>\nIddan Aaronsohn <mail@aniddan.com>\nGoogle Inc.\nJay Graves <jaywgraves@gmail.com>\nJeremy Jay <jeremy@pbnjay.com>\nJørgen Teunis <cayley@jorgenteunis.com>\nPius Uzamere <pius+github@alum.mit.edu>\nQlikTech International AB \nRobert Daniel Kortschak <dan.kortschak@adelaide.edu.au>\nRobert Melton <rmelton@gmail.com>\nStefan Koshiw <anonwasere@gmail.com>\nTimothy Armstrong <armstrong.timothy@gmail.com>\nYannic Bonenberger <contact@yannic-bonenberger.com>\nZihua Li <i@zihua.li>\nGaurav Tiwari <gaurav@gauravtiwari.co.uk>\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "*   @dennwc @eraserhd\n"
  },
  {
    "path": "CONTRIBUTORS",
    "content": "# The AUTHORS file lists the copyright holders; this file\n# lists people.  For example, Google employees are listed here\n# but not in AUTHORS, because Google holds the copyright.\n#\n# Names should be added to this file as:\n#     Name <email address>\n#\n# Please keep the list sorted.\n\nAlexander Peters <info@alexanderpeters.de>\nAndrew Dunham <andrew@du.nham.ca>\nBarak Michener <barakmich@google.com> <barak@cayley.io> <me@barakmich.com>\nBram Leenders <bcleenders@gmail.com>\nBrendan Ball <ball.brendan50@gmail.com>\nConnor Newton <connor@ifthenelse.io>\nDenys Smirnov <dennwc@pm.me>\nDerek Liang <fr.derekliang@gmail.com>\nGaurav Tiwari <gaurav@gauravtiwari.co.uk>\nIddan Aaronsohn <mail@aniddan.com>\nJay Graves <jaywgraves@gmail.com>\nJeremy Jay <jeremy@pbnjay.com>\nJørgen Teunis <cayley@jorgenteunis.com>\nOlaf Conradi <olaf@conradi.org>\nPius Uzamere <pius+github@alum.mit.edu>\nRobert Daniel Kortschak <dan.kortschak@adelaide.edu.au>\nRobert Melton <rmelton@gmail.com>\nStefan Koshiw <anonwasere@gmail.com>\nTad Adams <tad.adams@qlik.com>\nTimothy Armstrong <armstrong.timothy@gmail.com>\nTyler Gibbons <tyler.gibbons@ideogr.am>\nYannic Bonenberger <contact@yannic-bonenberger.com>\nZihua Li <i@zihua.li>\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:1.22 AS builder\n\nARG VERSION=v0.8.x-dev\n\n# Create filesystem for minimal image\nWORKDIR /fs\n\nRUN mkdir -p etc/ssl/certs lib/x86_64-linux-gnu tmp bin data; \\\n    # Copy CA Certificates\n    cp /etc/ssl/certs/ca-certificates.crt etc/ssl/certs/ca-certificates.crt; \\\n    # Copy C standard library\n    cp /lib/x86_64-linux-gnu/libc.* lib/x86_64-linux-gnu/\n\n# Set up workdir for compiling\nWORKDIR /src\n\n# Copy dependencies and install first\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Add all the other files\nADD . .\n\n# Pass a Git short SHA as build information to be used for displaying version\nRUN GIT_SHA=$(git rev-parse --short=12 HEAD); \\\n    go build \\\n    -ldflags=\"-linkmode external -extldflags -static -X github.com/cayleygraph/cayley/version.Version=$VERSION -X github.com/cayleygraph/cayley/version.GitHash=$GIT_SHA\" \\\n    -a \\\n    -installsuffix cgo \\\n    -o /fs/bin/cayley \\\n    -v \\\n    ./cmd/cayley\n\n# Move persisted configuration into filesystem\nRUN mv configurations/persisted.json /fs/etc/cayley.json\n\nWORKDIR /fs\n\n# Initialize bolt indexes file\nRUN ./bin/cayley init --config etc/cayley.json\n\nFROM scratch\n\n# Copy filesystem as root\nCOPY --from=builder /fs /\n\n# Define volume for configuration and data persistence. If you're using a\n# backend like bolt, make sure the file is saved to this directory.\nVOLUME [ \"/data\" ]\n\nEXPOSE 64210\n\nHEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"cayley\", \"health\" ]\n\n# Adding everything to entrypoint allows us to init, load and serve only with\n# arguments passed to docker run. For example:\n# `docker run cayleygraph/cayley --init -i /data/my_data.nq`\nENTRYPOINT [\"cayley\", \"http\", \"--host=:64210\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <a href=\"https://github.com/cayleygraph/cayley\">\n    <img width=\"200\" src=\"https://github.com/cayleygraph/branding/raw/master/cayley_bottom.png\" alt=\"Cayley\">\n  </a>\n</div>\n\n![Tests](https://github.com/cayleygraph/cayley/actions/workflows/build-and-release.yml/badge.svg)\n\nCayley is an open-source database for [Linked Data](https://www.w3.org/standards/semanticweb/data). It is inspired by the graph database behind Google's [Knowledge Graph](https://en.wikipedia.org/wiki/Knowledge_Graph) (formerly [Freebase](https://en.wikipedia.org/wiki/Freebase_(database))).\n\n[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/cayley)\n\n## [Documentation](https://cayley.gitbook.io/cayley/)\n\n## Features\n\n- Built-in query editor, visualizer and REPL\n- Multiple query languages:\n  - [Gizmo](./docs/gizmoapi.md): query language inspired by [Gremlin](https://tinkerpop.apache.org/gremlin.html)\n  - [GraphQL](./docs/graphql.md)-inspired query language\n  - [MQL](./docs/mql.md): simplified version for [Freebase](https://en.wikipedia.org/wiki/Freebase_(database)) fans\n- Modular: easy to connect to your favorite programming languages and back-end stores\n- Production ready: well tested and used by various companies for their production workloads\n- Fast: optimized specifically for usage in applications\n\n### Performance\n\nRough performance testing shows that, on 2014 consumer hardware and an average disk, 134m quads in LevelDB is no problem and a multi-hop intersection query -- films starring X and Y -- takes ~150ms.\n\n## Community\n\n- Website: [cayley.io](https://cayley.io)\n- Slack: [cayleygraph.slack.com](https://cayleygraph.slack.com) -- Invite [here](https://cayley-slackin.herokuapp.com/)\n- Discourse list: [discourse.cayley.io](https://discourse.cayley.io) (Also acts as mailing list, enable mailing list mode)\n- Twitter: [@cayleygraph](https://twitter.com/cayleygraph)\n- Involvement: [Contribute](./docs/contributing.md)\n"
  },
  {
    "path": "cayley_example.yml",
    "content": "store:\n  # backend to use\n  backend: bolt\n  # address or path for the database\n  address: \"./cayley.db\"\n  # open database in read-only mode\n  read_only: false\n  # backend-specific options\n  options:\n    nosync: false\nquery:\n  timeout: 30s\nload:\n  ignore_duplicates: false\n  ignore_missing: false\n  batch: 10000"
  },
  {
    "path": "client/client.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n)\n\nfunc New(addr string) *Client {\n\treturn &Client{addr: addr, cli: http.DefaultClient}\n}\n\n// Client is a struct used for communicating with a Cayley server through HTTP\n// Deprecated: Client exists for backwards compatability. New code should use\n// the updated client github.com/cayleygraph/go-client\ntype Client struct {\n\taddr string\n\tcli  *http.Client\n}\n\nfunc (c *Client) SetHTTPClient(cli *http.Client) {\n\tc.cli = cli\n}\nfunc (c *Client) url(s string, q map[string]string) string {\n\taddr := c.addr + s\n\tif len(q) != 0 {\n\t\tp := make(url.Values, len(q))\n\t\tfor k, v := range q {\n\t\t\tp.Set(k, v)\n\t\t}\n\t\taddr += \"?\" + p.Encode()\n\t}\n\treturn addr\n}\n\ntype errRequestFailed struct {\n\tStatus     string\n\tStatusCode int\n}\n\nfunc (e errRequestFailed) Error() string {\n\treturn fmt.Sprintf(\"request failed: %d %v\", e.StatusCode, e.Status)\n}\nfunc (c *Client) QuadReader() (quad.ReadCloser, error) {\n\tresp, err := http.Get(c.url(\"/api/v2/read\", map[string]string{\n\t\t\"format\": \"pquads\",\n\t}))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, errRequestFailed{StatusCode: resp.StatusCode, Status: resp.Status}\n\t}\n\tr := pquads.NewReader(resp.Body, 10*1024*1024)\n\tr.SetCloser(resp.Body)\n\treturn r, nil\n}\n\ntype funcCloser struct {\n\tf      func() error\n\tclosed bool\n}\n\nfunc (c funcCloser) Close() error {\n\tif c.closed {\n\t\treturn nil\n\t}\n\tc.closed = true\n\treturn c.f()\n}\nfunc (c *Client) QuadWriter() (quad.WriteCloser, error) {\n\tpr, pw := io.Pipe()\n\treq, err := http.NewRequest(\"POST\", c.url(\"/api/v2/write\", nil), pr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Content-Type\", pquads.ContentType)\n\terrc := make(chan error, 1)\n\tgo func() {\n\t\tdefer func() {\n\t\t\tclose(errc)\n\t\t\tpr.Close()\n\t\t}()\n\t\tresp, err := c.cli.Do(req)\n\t\tif resp != nil && resp.Body != nil {\n\t\t\tdefer resp.Body.Close()\n\t\t}\n\t\tif err == nil && resp.StatusCode != http.StatusOK {\n\t\t\terr = errRequestFailed{StatusCode: resp.StatusCode, Status: resp.Status}\n\t\t}\n\t\terrc <- err\n\t}()\n\tqw := pquads.NewWriter(pw, &pquads.Options{\n\t\tFull:   false,\n\t\tStrict: false,\n\t})\n\tqw.SetCloser(funcCloser{f: func() error {\n\t\tpw.Close()\n\t\treturn <-errc\n\t}})\n\treturn qw, nil\n}\n"
  },
  {
    "path": "clog/clog.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package clog provides a logging interface for cayley packages.\npackage clog\n\nimport \"log\"\n\n// Logger is the clog logging interface.\ntype Logger interface {\n\tInfof(format string, args ...interface{})\n\tWarningf(format string, args ...interface{})\n\tErrorf(format string, args ...interface{})\n\tFatalf(format string, args ...interface{})\n\tV(int) bool\n\tSetV(level int)\n}\n\nvar logger Logger = &stdlog{\n\tverbosity: 0,\n}\n\n// SetLogger set the clog logging implementation.\nfunc SetLogger(l Logger) { logger = l }\n\nvar verbosity int\n\n// V returns whether the current clog verbosity is above the specified level.\nfunc V(level int) bool {\n\tif logger == nil {\n\t\treturn false\n\t}\n\treturn logger.V(level)\n}\n\n// SetV sets the clog verbosity level.\nfunc SetV(level int) {\n\tif logger != nil {\n\t\tlogger.SetV(level)\n\t}\n}\n\n// Infof logs information level messages.\nfunc Infof(format string, args ...interface{}) {\n\tif logger != nil {\n\t\tlogger.Infof(format, args...)\n\t}\n}\n\n// Warningf logs warning level messages.\nfunc Warningf(format string, args ...interface{}) {\n\tif logger != nil {\n\t\tlogger.Warningf(format, args...)\n\t}\n}\n\n// Errorf logs error level messages.\nfunc Errorf(format string, args ...interface{}) {\n\tif logger != nil {\n\t\tlogger.Errorf(format, args...)\n\t}\n}\n\n// Fatalf logs fatal messages and terminates the program.\nfunc Fatalf(format string, args ...interface{}) {\n\tif logger != nil {\n\t\tlogger.Fatalf(format, args...)\n\t}\n}\n\n// stdlog wraps the standard library logger.\ntype stdlog struct {\n\tverbosity int\n}\n\nfunc (stdlog) Infof(format string, args ...interface{})    { log.Printf(format, args...) }\nfunc (stdlog) Warningf(format string, args ...interface{}) { log.Printf(\"WARN: \"+format, args...) }\nfunc (stdlog) Errorf(format string, args ...interface{})   { log.Printf(\"ERROR: \"+format, args...) }\nfunc (stdlog) Fatalf(format string, args ...interface{})   { log.Fatalf(\"FATAL: \"+format, args...) }\nfunc (s stdlog) V(level int) bool                          { return s.verbosity >= level }\nfunc (s *stdlog) SetV(level int)                           { s.verbosity = level }\n"
  },
  {
    "path": "clog/glog/glog.go",
    "content": "package glog\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/golang/glog\"\n)\n\nfunc init() {\n\tclog.SetLogger(Logger{})\n}\n\ntype Logger struct{}\n\nfunc (Logger) Infof(format string, args ...interface{}) {\n\tglog.InfoDepth(3, fmt.Sprintf(format, args...))\n}\nfunc (Logger) Warningf(format string, args ...interface{}) {\n\tglog.WarningDepth(3, fmt.Sprintf(format, args...))\n}\nfunc (Logger) Errorf(format string, args ...interface{}) {\n\tglog.ErrorDepth(3, fmt.Sprintf(format, args...))\n}\nfunc (Logger) Fatalf(format string, args ...interface{}) {\n\tglog.FatalDepth(3, fmt.Sprintf(format, args...))\n}\n\nfunc (Logger) V(level int) bool {\n\treturn bool(glog.V(glog.Level(level)))\n}\n\nfunc (Logger) SetV(v int) {\n\tglog.Warningf(\"changing log level is not supported; run command with '-v %d' flag\", v)\n}\n"
  },
  {
    "path": "cmd/cayley/cayley.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// +build !appengine\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/cmd/cayley/command\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t_ \"github.com/cayleygraph/cayley/clog/glog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/version\"\n\t\"github.com/cayleygraph/quad\"\n\n\t// Load supported backends\n\t_ \"github.com/cayleygraph/cayley/graph/all\"\n\n\t// Load all supported quad formats.\n\t_ \"github.com/cayleygraph/quad/dot\"\n\t_ \"github.com/cayleygraph/quad/gml\"\n\t_ \"github.com/cayleygraph/quad/graphml\"\n\t_ \"github.com/cayleygraph/quad/json\"\n\t_ \"github.com/cayleygraph/quad/jsonld\"\n\t_ \"github.com/cayleygraph/quad/nquads\"\n\t_ \"github.com/cayleygraph/quad/pquads\"\n\n\t// Load writer registry\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\n\t// Load supported query languages\n\t_ \"github.com/cayleygraph/cayley/query/gizmo\"\n\t_ \"github.com/cayleygraph/cayley/query/graphql\"\n\t_ \"github.com/cayleygraph/cayley/query/mql\"\n\t_ \"github.com/cayleygraph/cayley/query/sexp\"\n)\n\nvar (\n\trootCmd = &cobra.Command{\n\t\tUse:   \"cayley\",\n\t\tShort: \"Cayley is a graph store and graph query layer.\",\n\t\tPersistentPreRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tclog.Infof(\"Cayley version: %s (%s)\", version.Version, version.GitHash)\n\t\t\tif conf, _ := cmd.Flags().GetString(\"config\"); conf != \"\" {\n\t\t\t\tviper.SetConfigFile(conf)\n\t\t\t}\n\t\t\terr := viper.ReadInConfig()\n\t\t\tif _, ok := err.(viper.ConfigFileNotFoundError); !ok && err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif conf := viper.ConfigFileUsed(); conf != \"\" {\n\t\t\t\twd, _ := os.Getwd()\n\t\t\t\tif rel, _ := filepath.Rel(wd, conf); rel != \"\" && strings.Count(rel, \"..\") < 3 {\n\t\t\t\t\tconf = rel\n\t\t\t\t}\n\t\t\t\tclog.Infof(\"using config file: %s\", conf)\n\t\t\t}\n\t\t\t// force viper to load flags to variables\n\t\t\tgraph.IgnoreDuplicates = viper.GetBool(\"load.ignore_duplicates\")\n\t\t\tgraph.IgnoreMissing = viper.GetBool(\"load.ignore_missing\")\n\t\t\tquad.DefaultBatch = viper.GetInt(\"load.batch\")\n\t\t\tif host, _ := cmd.Flags().GetString(\"pprof\"); host != \"\" {\n\t\t\t\tgo func() {\n\t\t\t\t\tif err := http.ListenAndServe(host, nil); err != nil {\n\t\t\t\t\t\tclog.Errorf(\"failed to run pprof handler: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t\tif host, _ := cmd.Flags().GetString(\"metrics\"); host != \"\" {\n\t\t\t\tgo func() {\n\t\t\t\t\tif err := http.ListenAndServe(host, promhttp.Handler()); err != nil {\n\t\t\t\t\t\tclog.Errorf(\"failed to run metrics handler: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\tversionCmd = &cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: \"Prints the version of Cayley.\",\n\t\t// do not execute any persistent actions\n\t\tPersistentPreRun: func(cmd *cobra.Command, args []string) {},\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfmt.Println(\"Cayley version:\", version.Version)\n\t\t\tfmt.Println(\"Git commit hash:\", version.GitHash)\n\t\t\tif version.BuildDate != \"\" {\n\t\t\t\tfmt.Println(\"Build date:\", version.BuildDate)\n\t\t\t}\n\t\t},\n\t}\n)\n\ntype pFlag struct {\n\tflag.Value\n}\n\nfunc (pFlag) Type() string { return \"string\" }\n\nfunc init() {\n\t// set config names and paths\n\tviper.SetConfigName(\"cayley\")\n\tviper.SetEnvPrefix(\"cayley\")\n\tviper.AddConfigPath(\".\")\n\tviper.AddConfigPath(\"$HOME/.cayley/\")\n\tviper.AddConfigPath(\"/etc/\")\n\tif conf := os.Getenv(\"CAYLEY_CFG\"); conf != \"\" {\n\t\tviper.SetConfigFile(conf)\n\t}\n\n\trootCmd.AddCommand(\n\t\tversionCmd,\n\t\tcommand.NewInitDatabaseCmd(),\n\t\tcommand.NewLoadDatabaseCmd(),\n\t\tcommand.NewDumpDatabaseCmd(),\n\t\tcommand.NewUpgradeCmd(),\n\t\tcommand.NewReplCmd(),\n\t\tcommand.NewQueryCmd(),\n\t\tcommand.NewHTTPCmd(),\n\t\tcommand.NewConvertCmd(),\n\t\tcommand.NewDedupCommand(),\n\t\tcommand.NewHealthCmd(),\n\t\tcommand.NewSchemaCommand(),\n\t)\n\trootCmd.PersistentFlags().StringP(\"config\", \"c\", \"\", \"path to an explicit configuration file\")\n\n\tqnames := graph.QuadStores()\n\trootCmd.PersistentFlags().StringP(\"db\", \"d\", \"memstore\", \"database backend to use: \"+strings.Join(qnames, \", \"))\n\trootCmd.PersistentFlags().StringP(\"dbpath\", \"a\", \"\", \"path or address string for database\")\n\trootCmd.PersistentFlags().Bool(\"read_only\", false, \"open database in read-only mode\")\n\n\trootCmd.PersistentFlags().Bool(\"dup\", true, \"don't stop loading on duplicated on add\")\n\trootCmd.PersistentFlags().Bool(\"missing\", false, \"don't stop loading on missing key on delete\")\n\trootCmd.PersistentFlags().Int(\"batch\", quad.DefaultBatch, \"size of quads batch to load at once\")\n\n\trootCmd.PersistentFlags().String(\"memprofile\", \"\", \"path to output memory profile\")\n\trootCmd.PersistentFlags().String(\"cpuprofile\", \"\", \"path to output cpu profile\")\n\n\trootCmd.PersistentFlags().String(\"pprof\", \"\", \"host to serve pprof on (disabled by default)\")\n\trootCmd.PersistentFlags().String(\"metrics\", \"\", \"host to serve metrics on (disabled by default)\")\n\n\t// bind flags to config variables\n\tviper.BindPFlag(command.KeyBackend, rootCmd.PersistentFlags().Lookup(\"db\"))\n\tviper.BindPFlag(command.KeyAddress, rootCmd.PersistentFlags().Lookup(\"dbpath\"))\n\tviper.BindPFlag(command.KeyReadOnly, rootCmd.PersistentFlags().Lookup(\"read_only\"))\n\tviper.BindPFlag(\"load.ignore_duplicates\", rootCmd.PersistentFlags().Lookup(\"dup\"))\n\tviper.BindPFlag(\"load.ignore_missing\", rootCmd.PersistentFlags().Lookup(\"missing\"))\n\tviper.BindPFlag(command.KeyLoadBatch, rootCmd.PersistentFlags().Lookup(\"batch\"))\n\n\t// make both store.path and store.address work\n\tviper.RegisterAlias(command.KeyPath, command.KeyAddress)\n\t// aliases for legacy config files\n\tviper.RegisterAlias(\"database\", command.KeyBackend)\n\tviper.RegisterAlias(\"db_path\", command.KeyAddress)\n\tviper.RegisterAlias(\"read_only\", command.KeyReadOnly)\n\tviper.RegisterAlias(\"db_options\", command.KeyOptions)\n\n\t{ // re-register standard Go flags to cobra\n\t\trf := rootCmd.PersistentFlags()\n\t\tflag.CommandLine.VisitAll(func(f *flag.Flag) {\n\t\t\tswitch f.Name {\n\t\t\tcase \"v\": // glog.v\n\t\t\t\trf.VarP(pFlag{f.Value}, \"verbose\", \"v\", f.Usage)\n\t\t\tcase \"vmodule\": // glog.vmodule\n\t\t\t\trf.Var(pFlag{f.Value}, \"vmodule\", f.Usage)\n\t\t\tcase \"log_backtrace_at\": // glog.log_backtrace_at\n\t\t\t\trf.Var(pFlag{f.Value}, \"backtrace\", f.Usage)\n\t\t\tcase \"stderrthreshold\": // glog.stderrthreshold\n\t\t\t\trf.VarP(pFlag{f.Value}, \"log\", \"l\", f.Usage)\n\t\t\tcase \"alsologtostderr\": // glog.alsologtostderr\n\t\t\t\trf.Var(pFlag{f.Value}, f.Name, f.Usage)\n\t\t\tcase \"logtostderr\": // glog.logtostderr\n\t\t\t\tf.Value.Set(\"true\")\n\t\t\t\trf.Var(pFlag{f.Value}, f.Name, f.Usage)\n\t\t\tcase \"log_dir\": // glog.log_dir\n\t\t\t\trf.Var(pFlag{f.Value}, \"logs\", f.Usage)\n\t\t\t}\n\t\t})\n\t\t// make sure flags parsed flag is set - parse empty args\n\t\tflag.CommandLine = flag.NewFlagSet(\"\", flag.ContinueOnError)\n\t\tflag.CommandLine.Parse([]string{\"\"})\n\t}\n}\n\nfunc main() {\n\tif err := rootCmd.Execute(); err != nil {\n\t\tclog.Errorf(\"%v\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/cayley/command/convert.go",
    "content": "package command\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/internal\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc newLazyReader(open func() (quad.ReadCloser, error)) quad.ReadCloser {\n\treturn &lazyReader{open: open}\n}\n\ntype lazyReader struct {\n\trc   quad.ReadCloser\n\topen func() (quad.ReadCloser, error)\n}\n\nfunc (r *lazyReader) ReadQuad() (quad.Quad, error) {\n\tif r.rc == nil {\n\t\trc, err := r.open()\n\t\tif err != nil {\n\t\t\treturn quad.Quad{}, err\n\t\t}\n\t\tr.rc = rc\n\t}\n\treturn r.rc.ReadQuad()\n}\nfunc (r *lazyReader) Close() (err error) {\n\tif r.rc != nil {\n\t\terr = r.rc.Close()\n\t}\n\treturn\n}\n\ntype multiReader struct {\n\trc []quad.ReadCloser\n\ti  int\n}\n\nfunc (r *multiReader) ReadQuad() (quad.Quad, error) {\n\tfor {\n\t\tif r.i >= len(r.rc) {\n\t\t\treturn quad.Quad{}, io.EOF\n\t\t}\n\t\trc := r.rc[r.i]\n\t\tq, err := rc.ReadQuad()\n\t\tif err == io.EOF {\n\t\t\trc.Close()\n\t\t\tr.i++\n\t\t\tcontinue\n\t\t}\n\t\treturn q, err\n\t}\n}\nfunc (r *multiReader) Close() error {\n\tvar first error\n\tif r.i < len(r.rc) {\n\t\tfor _, rc := range r.rc[r.i:] {\n\t\t\tif err := rc.Close(); err != nil && first == nil {\n\t\t\t\tfirst = err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc NewConvertCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:     \"convert\",\n\t\tAliases: []string{\"conv\"},\n\t\tShort:   \"Convert quad files between supported formats.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tdump, _ := cmd.Flags().GetString(flagDump)\n\t\t\tdumpf, _ := cmd.Flags().GetString(flagDumpFormat)\n\t\t\tif dump == \"\" && len(args) > 0 {\n\t\t\t\ti := len(args) - 1\n\t\t\t\tdump, args = args[i], args[:i]\n\t\t\t}\n\n\t\t\tvar files []string\n\t\t\tif load, _ := cmd.Flags().GetString(flagLoad); load != \"\" {\n\t\t\t\tfiles = append(files, load)\n\t\t\t}\n\t\t\tfiles = append(files, args...)\n\t\t\tif len(files) == 0 || dump == \"\" {\n\t\t\t\treturn errors.New(\"both input and output files must be specified\")\n\t\t\t}\n\t\t\tloadf, _ := cmd.Flags().GetString(flagLoadFormat)\n\t\t\tvar multi multiReader\n\t\t\tfor _, path := range files {\n\t\t\t\tpath := path\n\t\t\t\tmulti.rc = append(multi.rc, newLazyReader(func() (quad.ReadCloser, error) {\n\t\t\t\t\tif dump == \"-\" {\n\t\t\t\t\t\tclog.Infof(\"reading %q\", path)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfmt.Printf(\"reading %q\\n\", path)\n\t\t\t\t\t}\n\t\t\t\t\treturn internal.QuadReaderFor(path, loadf)\n\t\t\t\t}))\n\t\t\t}\n\t\t\t// TODO: print additional stats\n\t\t\treturn writerQuadsTo(dump, dumpf, &multi)\n\t\t},\n\t}\n\tregisterLoadFlags(cmd)\n\tregisterDumpFlags(cmd)\n\treturn cmd\n}\n"
  },
  {
    "path": "cmd/cayley/command/database.go",
    "content": "package command\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"runtime/pprof\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/internal\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nconst (\n\tKeyBackend  = \"store.backend\"\n\tKeyAddress  = \"store.address\"\n\tKeyPath     = \"store.path\"\n\tKeyReadOnly = \"store.read_only\"\n\tKeyOptions  = \"store.options\"\n\n\tKeyLoadBatch = \"load.batch\"\n)\n\nconst (\n\tflagLoad       = \"load\"\n\tflagLoadFormat = \"load_format\"\n\tflagDump       = \"dump\"\n\tflagDumpFormat = \"dump_format\"\n)\n\nvar ErrNotPersistent = errors.New(\"database type is not persistent\")\n\nfunc registerLoadFlags(cmd *cobra.Command) {\n\t// TODO: allow to load multiple files\n\tcmd.Flags().StringP(flagLoad, \"i\", \"\", `quad file to load after initialization (\".gz\" supported, \"-\" for stdin)`)\n\tvar names []string\n\tfor _, f := range quad.Formats() {\n\t\tif f.Reader != nil {\n\t\t\tnames = append(names, f.Name)\n\t\t}\n\t}\n\tsort.Strings(names)\n\tcmd.Flags().String(flagLoadFormat, \"\", `quad file format to use for loading instead of auto-detection (\"`+strings.Join(names, `\", \"`)+`\")`)\n}\n\nfunc registerDumpFlags(cmd *cobra.Command) {\n\tcmd.Flags().StringP(flagDump, \"o\", \"\", `quad file to dump the database to (\".gz\" supported, \"-\" for stdout)`)\n\tvar names []string\n\tfor _, f := range quad.Formats() {\n\t\tif f.Writer != nil {\n\t\t\tnames = append(names, f.Name)\n\t\t}\n\t}\n\tsort.Strings(names)\n\tcmd.Flags().String(flagDumpFormat, \"\", `quad file format to use instead of auto-detection (\"`+strings.Join(names, `\", \"`)+`\")`)\n}\n\nfunc NewInitDatabaseCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"init\",\n\t\tShort: \"Create an empty database.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tname := viper.GetString(KeyBackend)\n\t\t\tif graph.IsRegistered(name) && !graph.IsPersistent(name) {\n\t\t\t\treturn ErrNotPersistent\n\t\t\t}\n\t\t\t// TODO: maybe check read-only flag in config before that?\n\t\t\tif err := initDatabase(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\treturn cmd\n}\n\nfunc NewLoadDatabaseCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"load\",\n\t\tShort: \"Bulk-load a quad file into the database.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tp := mustSetupProfile(cmd)\n\t\t\tdefer mustFinishProfile(p)\n\t\t\tload, _ := cmd.Flags().GetString(flagLoad)\n\t\t\tif load == \"\" && len(args) == 1 {\n\t\t\t\tload = args[0]\n\t\t\t}\n\t\t\tif load == \"\" {\n\t\t\t\treturn errors.New(\"one quads file must be specified\")\n\t\t\t}\n\t\t\tif init, err := cmd.Flags().GetBool(\"init\"); err != nil {\n\t\t\t\treturn err\n\t\t\t} else if init {\n\t\t\t\tif err = initDatabase(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\th, err := openDatabase()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\tqw, err := h.NewQuadWriter()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer qw.Close()\n\n\t\t\t// TODO: check read-only flag in config before that?\n\t\t\ttyp, _ := cmd.Flags().GetString(flagLoadFormat)\n\t\t\tif err = internal.Load(qw, quad.DefaultBatch, load, typ); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif dump, _ := cmd.Flags().GetString(flagDump); dump != \"\" {\n\t\t\t\ttyp, _ := cmd.Flags().GetString(flagDumpFormat)\n\t\t\t\tif err = dumpDatabase(h, dump, typ); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\tcmd.Flags().Bool(\"init\", false, \"initialize the database before using it\")\n\tregisterLoadFlags(cmd)\n\tregisterDumpFlags(cmd)\n\treturn cmd\n}\n\nfunc NewDumpDatabaseCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"dump\",\n\t\tShort: \"Bulk-dump the database into a quad file.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tdump, _ := cmd.Flags().GetString(flagDump)\n\t\t\tif dump == \"\" && len(args) == 1 {\n\t\t\t\tdump = args[0]\n\t\t\t}\n\t\t\tif dump == \"\" {\n\t\t\t\tdump = \"-\"\n\t\t\t}\n\t\t\th, err := openDatabase()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\ttyp, _ := cmd.Flags().GetString(flagDumpFormat)\n\t\t\treturn dumpDatabase(h, dump, typ)\n\t\t},\n\t}\n\tregisterDumpFlags(cmd)\n\treturn cmd\n}\n\nfunc NewUpgradeCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"upgrade\",\n\t\tShort: \"Upgrade Cayley database to current supported format.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tname := viper.GetString(KeyBackend)\n\t\t\tif graph.IsRegistered(name) && !graph.IsPersistent(name) {\n\t\t\t\treturn ErrNotPersistent\n\t\t\t}\n\t\t\taddr := viper.GetString(KeyAddress)\n\t\t\topts := graph.Options(viper.GetStringMap(KeyOptions))\n\t\t\tclog.Infof(\"upgrading database...\")\n\t\t\treturn graph.UpgradeQuadStore(name, addr, opts)\n\t\t},\n\t}\n\treturn cmd\n}\n\nfunc printBackendInfo() {\n\tname := viper.GetString(KeyBackend)\n\tpath := viper.GetString(KeyAddress)\n\tif path != \"\" {\n\t\tpath = \" (\" + path + \")\"\n\t}\n\tclog.Infof(\"using backend %q%s\", name, path)\n}\n\nfunc initDatabase() error {\n\tname := viper.GetString(KeyBackend)\n\tpath := viper.GetString(KeyAddress)\n\topts := viper.GetStringMap(KeyOptions)\n\treturn graph.InitQuadStore(name, path, graph.Options(opts))\n}\n\nfunc openDatabase() (*graph.Handle, error) {\n\tname := viper.GetString(KeyBackend)\n\tpath := viper.GetString(KeyAddress)\n\topts := graph.Options(viper.GetStringMap(KeyOptions))\n\tqs, err := graph.NewQuadStore(name, path, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tqw, err := graph.NewQuadWriter(\"single\", qs, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &graph.Handle{QuadStore: qs, QuadWriter: qw}, nil\n}\n\nfunc openForQueries(cmd *cobra.Command) (*graph.Handle, error) {\n\tif init, err := cmd.Flags().GetBool(\"init\"); err != nil {\n\t\treturn nil, err\n\t} else if init {\n\t\tif err = initDatabase(); err == graph.ErrDatabaseExists {\n\t\t\tclog.Infof(\"database already initialized, skipping init\")\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar load string\n\th, err := openDatabase()\n\tif err == graph.ErrQuadStoreNotPersistent {\n\t\tload = viper.GetString(KeyAddress)\n\t\tviper.Set(KeyAddress, \"\")\n\t\th, err = openDatabase()\n\t}\n\tif err == graph.ErrQuadStoreNotPersistent {\n\t\treturn nil, fmt.Errorf(\"%v; did you mean -i flag?\", err)\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tif load2, _ := cmd.Flags().GetString(flagLoad); load2 != \"\" {\n\t\tif load != \"\" {\n\t\t\th.Close()\n\t\t\treturn nil, fmt.Errorf(\"both -a and -i flags cannot be specified\")\n\t\t}\n\t\tload = load2\n\t}\n\tif load != \"\" {\n\t\tqw, err := h.NewQuadWriter()\n\t\tif err != nil {\n\t\t\th.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer qw.Close()\n\n\t\ttyp, _ := cmd.Flags().GetString(flagLoadFormat)\n\t\t// TODO: check read-only flag in config before that?\n\t\tstart := time.Now()\n\t\tif err = internal.Load(qw, quad.DefaultBatch, load, typ); err != nil {\n\t\t\th.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tclog.Infof(\"loaded %q in %v\", load, time.Since(start))\n\t}\n\treturn h, nil\n}\n\ntype profileData struct {\n\tcpuProfile *os.File\n\tmemPath    string\n}\n\nfunc mustSetupProfile(cmd *cobra.Command) profileData {\n\tp := profileData{}\n\tmpp := cmd.Flag(\"memprofile\")\n\tp.memPath = mpp.Value.String()\n\tcpp := cmd.Flag(\"cpuprofile\")\n\tv := cpp.Value.String()\n\tif v != \"\" {\n\t\tf, err := os.Create(v)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Could not open CPU profile file %s\\n\", v)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tp.cpuProfile = f\n\t\tpprof.StartCPUProfile(f)\n\t}\n\treturn p\n}\n\nfunc mustFinishProfile(p profileData) {\n\tif p.cpuProfile != nil {\n\t\tpprof.StopCPUProfile()\n\t\tp.cpuProfile.Close()\n\t}\n\tif p.memPath != \"\" {\n\t\tf, err := os.Create(p.memPath)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Could not open memory profile file %s\\n\", p.memPath)\n\t\t\tos.Exit(1)\n\t\t}\n\t\truntime.GC()\n\t\tif err := pprof.WriteHeapProfile(f); err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Could not write memory profile file %s\\n\", p.memPath)\n\t\t}\n\t\tf.Close()\n\t}\n}\n"
  },
  {
    "path": "cmd/cayley/command/dedup.go",
    "content": "package command\n\nimport (\n\t\"context\"\n\t\"crypto/sha1\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n)\n\nfunc iriFlag(s string, err error) (quad.IRI, error) {\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn quad.IRI(s), nil\n}\n\nfunc NewDedupCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"dedup\",\n\t\tShort: \"Deduplicate bnode values\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tctx := context.Background()\n\t\t\tprintBackendInfo()\n\t\t\th, err := openDatabase()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\tpred, _ := iriFlag(cmd.Flags().GetString(\"pred\"))\n\t\t\ttyp, _ := iriFlag(cmd.Flags().GetString(\"type\"))\n\t\t\tif typ == \"\" {\n\t\t\t\treturn errors.New(\"no type is specified\")\n\t\t\t}\n\t\t\treturn dedupProperties(ctx, h, pred, typ)\n\t\t},\n\t}\n\tcmd.Flags().String(\"pred\", rdf.Type, \"type predicate to use to find nodes\")\n\tcmd.Flags().String(\"type\", \"\", \"type value to use to find nodes\")\n\treturn cmd\n}\n\nfunc valueLess(a, b graph.Ref) bool {\n\t// TODO(dennwc): more effective way\n\ts1, s2 := fmt.Sprint(a), fmt.Sprint(b)\n\treturn s1 < s2\n}\n\ntype sortVals []graph.Ref\n\nfunc (a sortVals) Len() int           { return len(a) }\nfunc (a sortVals) Less(i, j int) bool { return valueLess(a[i], a[j]) }\nfunc (a sortVals) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\n\ntype sortProp []property\n\nfunc (a sortProp) Len() int           { return len(a) }\nfunc (a sortProp) Less(i, j int) bool { return valueLess(a[i].Pred, a[j].Pred) }\nfunc (a sortProp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\n\nfunc hashProperties(h hash.Hash, m map[interface{}]property) string {\n\tprops := make([]property, 0, len(m))\n\tfor _, p := range m {\n\t\tif len(p.Values) > 1 {\n\t\t\tsort.Sort(sortVals(p.Values))\n\t\t}\n\t\tprops = append(props, p)\n\t}\n\tsort.Sort(sortProp(props))\n\th.Reset()\n\tfor _, p := range props {\n\t\tfmt.Fprint(h, p.Pred)\n\t\th.Write([]byte{0})\n\t\tfor _, v := range p.Values {\n\t\t\tfmt.Fprint(h, v)\n\t\t\th.Write([]byte{1})\n\t\t}\n\t}\n\tres := make([]byte, 0, h.Size())\n\tres = h.Sum(res)\n\treturn string(res)\n}\n\ntype property struct {\n\tPred   graph.Ref\n\tValues []graph.Ref\n}\n\nfunc dedupProperties(ctx context.Context, h *graph.Handle, pred, typ quad.IRI) error {\n\tbatch := viper.GetInt(KeyLoadBatch)\n\tif batch == 0 {\n\t\tbatch = quad.DefaultBatch\n\t}\n\n\tqs := h.QuadStore\n\tp := path.StartPath(qs).Has(pred, typ)\n\tictx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\tvar gerr error\n\n\tseen := make(map[string]graph.Ref)\n\tcnt, dedup := 0, 0\n\tstart := time.Now()\n\tlast := start\n\thh := sha1.New()\n\n\ttx := graph.NewTransaction()\n\ttxn := 0\n\tflush := func() {\n\t\tif txn == 0 {\n\t\t\treturn\n\t\t}\n\t\terr := h.ApplyTransaction(tx)\n\t\tif err == nil {\n\t\t\ttx = graph.NewTransaction()\n\t\t\tdedup += txn\n\t\t\ttxn = 0\n\t\t} else {\n\t\t\tgerr = err\n\t\t\tcancel()\n\t\t}\n\t\tif now := time.Now(); now.Sub(last) > time.Second*5 {\n\t\t\tlast = now\n\t\t\tclog.Infof(\"deduplicated %d/%d nodes (%.1f nodes/sec)\",\n\t\t\t\tdedup, cnt, float64(cnt)/now.Sub(start).Seconds(),\n\t\t\t)\n\t\t}\n\t}\n\terr := p.Iterate(ictx).Each(func(s graph.Ref) error {\n\t\tcnt++\n\t\tit := qs.QuadIterator(quad.Subject, s).Iterate()\n\t\tdefer it.Close()\n\t\tm := make(map[interface{}]property)\n\t\tfor it.Next(ictx) {\n\t\t\tq := it.Result()\n\t\t\tp, err := qs.QuadDirection(q, quad.Predicate)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\to, err := qs.QuadDirection(q, quad.Object)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tk := refs.ToKey(p)\n\t\t\tprop := m[k]\n\t\t\tprop.Pred = p\n\t\t\tprop.Values = append(prop.Values, o)\n\t\t\tm[k] = prop\n\t\t}\n\t\tif gerr = it.Err(); gerr != nil {\n\t\t\tcancel()\n\t\t}\n\t\tph := hashProperties(hh, m)\n\t\tid, ok := seen[ph]\n\t\tif !ok {\n\t\t\tseen[ph] = s\n\t\t\treturn nil\n\t\t}\n\t\tif gerr = dedupValueTx(ictx, h, tx, s, id); gerr != nil {\n\t\t\tcancel()\n\t\t}\n\t\ttxn++\n\t\tif txn >= batch { // TODO(dennwc): flag\n\t\t\tflush()\n\t\t}\n\t\treturn nil\n\t})\n\tflush()\n\tclog.Infof(\"deduplicated %d/%d nodes in %v\", dedup, cnt, time.Since(start))\n\tif gerr != nil {\n\t\terr = gerr\n\t}\n\treturn err\n}\n\nfunc dedupValueTx(ctx context.Context, h *graph.Handle, tx *graph.Transaction, a, b graph.Ref) error {\n\tv, err := h.NameOf(b)\n\tif err != nil {\n\t\treturn err\n\t}\n\tit := h.QuadIterator(quad.Object, a).Iterate()\n\tdefer it.Close()\n\tfor it.Next(ctx) {\n\t\t// TODO(dennwc): we should be able to add \"raw\" quads without getting values for directions\n\t\tq, err := h.Quad(it.Result())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttx.RemoveQuad(q)\n\t\tq.Object = v\n\t\ttx.AddQuad(q)\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn err\n\t}\n\tit.Close()\n\n\tit = h.QuadIterator(quad.Subject, a).Iterate()\n\tdefer it.Close()\n\tfor it.Next(ctx) {\n\t\tq, err := h.Quad(it.Result())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttx.RemoveQuad(q)\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/cayley/command/dump.go",
    "content": "package command\n\nimport (\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc writerQuadsTo(path string, typ string, qr quad.Reader) error {\n\tvar f *os.File\n\tif path == \"-\" {\n\t\tf = os.Stdout\n\t\tclog.Infof(\"writing quads to stdout\")\n\t} else {\n\t\tvar err error\n\t\tf, err = os.Create(path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create file %q: %v\", path, err)\n\t\t}\n\t\tdefer f.Close()\n\t\tfmt.Printf(\"writing quads to file %q\\n\", path)\n\t}\n\n\tvar w io.Writer = f\n\text := filepath.Ext(path)\n\tif ext == \".gz\" {\n\t\text = filepath.Ext(strings.TrimSuffix(path, ext))\n\t\tgzip := gzip.NewWriter(f)\n\t\tdefer gzip.Close()\n\t\tw = gzip\n\t}\n\tvar format *quad.Format\n\tif typ == \"\" {\n\t\tformat = quad.FormatByExt(ext)\n\t\tif format == nil {\n\t\t\ttyp = \"nquads\"\n\t\t}\n\t}\n\tif format == nil {\n\t\tformat = quad.FormatByName(typ)\n\t}\n\tif format == nil {\n\t\treturn fmt.Errorf(\"unsupported format: %q\", typ)\n\t} else if format.Writer == nil {\n\t\treturn fmt.Errorf(\"encoding in %s format is not supported\", typ)\n\t}\n\tqw := format.Writer(w)\n\tdefer qw.Close()\n\n\tn, err := quad.Copy(qw, qr)\n\tif err != nil {\n\t\treturn err\n\t} else if err = qw.Close(); err != nil {\n\t\treturn err\n\t}\n\tif path != \"-\" {\n\t\tfmt.Printf(\"%d entries were written\\n\", n)\n\t}\n\treturn nil\n}\n\nfunc dumpDatabase(h *graph.Handle, path string, typ string) error {\n\t//TODO: add possible support for exporting specific queries only\n\tqr := graph.NewQuadStoreReader(h.QuadStore)\n\tdefer qr.Close()\n\treturn writerQuadsTo(path, typ, qr)\n}\n"
  },
  {
    "path": "cmd/cayley/command/health.go",
    "content": "package command\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst defaultAddress = \"http://localhost:64210/\"\n\nfunc NewHealthCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:     \"health\",\n\t\tAliases: []string{},\n\t\tShort:   \"Health check HTTP server\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif len(args) > 1 {\n\t\t\t\treturn fmt.Errorf(\"Too many arguments provided, expected 0 or 1\")\n\t\t\t}\n\t\t\taddress := defaultAddress\n\t\t\tif len(args) == 1 {\n\t\t\t\taddress = args[0]\n\t\t\t}\n\t\t\thealthAddress := address + \"health\"\n\t\t\tresp, err := http.Get(healthAddress)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer resp.Body.Close()\n\t\t\tif resp.StatusCode != 204 {\n\t\t\t\treturn fmt.Errorf(\"/health responded with status code %d, expected 204\", resp.StatusCode)\n\t\t\t}\n\t\t\tlog.Printf(\"%s ok\", healthAddress)\n\t\t\treturn nil\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "cmd/cayley/command/http.go",
    "content": "package command\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\tchttp \"github.com/cayleygraph/cayley/internal/http\"\n)\n\nfunc NewHTTPCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"http\",\n\t\tShort: \"Serve an HTTP endpoint on the given host and port.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tp := mustSetupProfile(cmd)\n\t\t\tdefer mustFinishProfile(p)\n\n\t\t\th, err := openForQueries(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\terr = chttp.SetupRoutes(h, &chttp.Config{\n\t\t\t\tTimeout:  viper.GetDuration(keyQueryTimeout),\n\t\t\t\tReadOnly: viper.GetBool(KeyReadOnly),\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\thost, _ := cmd.Flags().GetString(\"host\")\n\t\t\tphost := host\n\t\t\tif host, port, err := net.SplitHostPort(host); err == nil && host == \"\" {\n\t\t\t\tphost = net.JoinHostPort(\"localhost\", port)\n\t\t\t}\n\t\t\tclog.Infof(\"listening on %s, web interface at http://%s\", host, phost)\n\t\t\treturn http.ListenAndServe(host, nil)\n\t\t},\n\t}\n\tcmd.Flags().String(\"host\", \"127.0.0.1:64210\", \"host:port to listen on\")\n\tcmd.Flags().Bool(\"init\", false, \"initialize the database before using it\")\n\tcmd.Flags().DurationP(\"timeout\", \"t\", 30*time.Second, \"elapsed time until an individual query times out\")\n\tregisterLoadFlags(cmd)\n\tviper.BindPFlag(keyQueryTimeout, cmd.Flags().Lookup(\"timeout\"))\n\treturn cmd\n}\n"
  },
  {
    "path": "cmd/cayley/command/repl.go",
    "content": "package command\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/internal/repl\"\n\t\"github.com/cayleygraph/cayley/query\"\n)\n\nconst (\n\tkeyQueryTimeout = \"query.timeout\"\n)\n\nfunc getContext() (context.Context, func()) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tch := make(chan os.Signal, 1)\n\tsignal.Notify(ch, os.Interrupt)\n\tgo func() {\n\t\tselect {\n\t\tcase <-ch:\n\t\tcase <-ctx.Done():\n\t\t}\n\t\tsignal.Stop(ch)\n\t\tcancel()\n\t}()\n\treturn ctx, cancel\n}\n\nfunc registerQueryFlags(cmd *cobra.Command) {\n\tlangs := query.Languages()\n\tcmd.Flags().Bool(\"init\", false, \"initialize the database before using it\")\n\tcmd.Flags().String(\"lang\", \"gizmo\", `query language to use (\"`+strings.Join(langs, `\", \"`)+`\")`)\n\tcmd.Flags().DurationP(\"timeout\", \"t\", 30*time.Second, \"elapsed time until an individual query times out\")\n\tviper.BindPFlag(keyQueryTimeout, cmd.Flags().Lookup(\"timeout\"))\n\tregisterLoadFlags(cmd)\n}\n\nfunc NewReplCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"repl\",\n\t\tShort: \"Drop into a REPL of the given query language.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprintBackendInfo()\n\t\t\tp := mustSetupProfile(cmd)\n\t\t\tdefer mustFinishProfile(p)\n\n\t\t\th, err := openForQueries(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\tctx, cancel := getContext()\n\t\t\tdefer cancel()\n\n\t\t\ttimeout := viper.GetDuration(\"timeout\")\n\t\t\tlang, _ := cmd.Flags().GetString(\"lang\")\n\t\t\treturn repl.Repl(ctx, h, lang, timeout)\n\t\t},\n\t}\n\tregisterQueryFlags(cmd)\n\treturn cmd\n}\n\nfunc NewQueryCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:     \"query\",\n\t\tAliases: []string{\"qu\"},\n\t\tShort:   \"Run a query in a specified database and print results.\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tvar querystr string\n\t\t\tif len(args) == 0 {\n\t\t\t\tbytes, err := ioutil.ReadAll(os.Stdin)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"error occured while reading from stdin : %s\", err)\n\t\t\t\t}\n\t\t\t\tquerystr = string(bytes)\n\t\t\t} else if len(args) == 1 {\n\t\t\t\tquerystr = args[0]\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"query accepts only one argument, the query string or nothing for reading from stdin\")\n\t\t\t}\n\t\t\tclog.Infof(\"Query:\\n%s\", querystr)\n\t\t\tprintBackendInfo()\n\t\t\tp := mustSetupProfile(cmd)\n\t\t\tdefer mustFinishProfile(p)\n\n\t\t\th, err := openForQueries(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer h.Close()\n\n\t\t\tctx, cancel := getContext()\n\t\t\tdefer cancel()\n\n\t\t\ttimeout := viper.GetDuration(\"timeout\")\n\t\t\tif timeout > 0 {\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, timeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\t\t\tlang, _ := cmd.Flags().GetString(\"lang\")\n\t\t\tlimit, err := cmd.Flags().GetInt(\"limit\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tenc := json.NewEncoder(os.Stdout)\n\t\t\tit, err := query.Execute(ctx, h, lang, querystr, query.Options{\n\t\t\t\tCollation: query.JSON,\n\t\t\t\tLimit:     limit,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer it.Close()\n\t\t\tfor i := 0; it.Next(ctx) && (limit <= 0 || i < limit); i++ {\n\t\t\t\tif err = enc.Encode(it.Result()); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn it.Err()\n\t\t},\n\t}\n\tregisterQueryFlags(cmd)\n\tcmd.Flags().IntP(\"limit\", \"n\", 100, \"limit a number of results\")\n\treturn cmd\n}\n"
  },
  {
    "path": "cmd/cayley/command/schema.go",
    "content": "package command\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/cayleygraph/cayley/internal/linkedql/schema\"\n)\n\nfunc NewSchemaCommand() *cobra.Command {\n\troot := &cobra.Command{\n\t\tUse:   \"schema\",\n\t\tShort: \"Commands related to RDF schema\",\n\t}\n\troot.AddCommand(\n\t\tNewLinkedQLSchemaCommand(),\n\t)\n\treturn root\n}\n\nfunc NewLinkedQLSchemaCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"linkedql\",\n\t\tShort: \"Generate LinkedQL Schema to stdout\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif len(args) > 0 {\n\t\t\t\treturn fmt.Errorf(\"too many arguments provided, expected 0\")\n\t\t\t}\n\t\t\tdata := schema.Generate()\n\t\t\tbuf := bytes.NewBuffer(nil)\n\t\t\terr := json.Indent(buf, data, \"\", \"\\t\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfmt.Println(buf)\n\t\t\treturn nil\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "cmd/cayleyexport/cayleyexport.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\n\t// Load all supported quad formats.\n\t\"github.com/cayleygraph/quad\"\n\t_ \"github.com/cayleygraph/quad/jsonld\"\n\t_ \"github.com/cayleygraph/quad/nquads\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst defaultFormat = \"jsonld\"\n\n// NewCmd creates the command\nfunc NewCmd() *cobra.Command {\n\tvar quiet bool\n\tvar uri, formatName, out string\n\n\tvar cmd = &cobra.Command{\n\t\tUse:   \"cayleyexport\",\n\t\tShort: \"Export data from Cayley. If no file is provided, cayleyexport writes to stdout.\",\n\t\tArgs:  cobra.NoArgs,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif quiet {\n\t\t\t\tclog.SetV(500)\n\t\t\t}\n\t\t\tvar format *quad.Format\n\t\t\tvar w io.Writer\n\t\t\tif formatName != \"\" {\n\t\t\t\tformat = quad.FormatByName(formatName)\n\t\t\t}\n\t\t\tif out == \"\" {\n\t\t\t\tw = cmd.OutOrStdout()\n\t\t\t} else {\n\t\t\t\tif formatName == \"\" {\n\t\t\t\t\tformat = formatByFileName(out)\n\t\t\t\t\tif format == nil {\n\t\t\t\t\t\tclog.Warningf(\"File has unknown extension %v. Defaulting to %v\", out, defaultFormat)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfile, err := os.Create(out)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tw = file\n\t\t\t\tdefer file.Close()\n\t\t\t}\n\t\t\tif format == nil {\n\t\t\t\tformat = quad.FormatByName(defaultFormat)\n\t\t\t}\n\t\t\treq, err := http.NewRequest(http.MethodGet, uri+\"/api/v2/read\", nil)\n\t\t\treq.Header.Set(\"Accept\", format.Mime[0])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tclient := &http.Client{}\n\t\t\tresp, err := client.Do(req)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer resp.Body.Close()\n\t\t\t_, err = io.Copy(w, resp.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(&uri, \"uri\", \"\", \"http://127.0.0.1:64210\", \"Cayley URI connection string\")\n\tcmd.Flags().StringVarP(&formatName, \"format\", \"\", \"\", \"format of the provided data (if can not be detected defaults to JSON-LD)\")\n\tcmd.Flags().StringVarP(&out, \"out\", \"o\", \"\", \"output file; if not specified, stdout is used\")\n\tcmd.Flags().BoolVarP(&quiet, \"quiet\", \"q\", false, \"hide all log output\")\n\n\treturn cmd\n}\n\nfunc main() {\n\tcmd := NewCmd()\n\tif err := cmd.Execute(); err != nil {\n\t\tos.Exit(1)\n\t}\n}\n\nfunc formatByFileName(fileName string) *quad.Format {\n\text := filepath.Ext(fileName)\n\treturn quad.FormatByExt(ext)\n}\n"
  },
  {
    "path": "cmd/cayleyexport/cayleyexport_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\tchttp \"github.com/cayleygraph/cayley/internal/http\"\n)\n\nvar testData = []quad.Quad{\n\t{\n\t\tSubject:   quad.IRI(\"http://example.com/alice\"),\n\t\tPredicate: quad.IRI(\"http://example.com/likes\"),\n\t\tObject:    quad.IRI(\"http://example.com/bob\"),\n\t\tLabel:     nil,\n\t},\n}\n\nfunc serializeTestData() string {\n\tbuf := bytes.NewBuffer(nil)\n\tw := jsonld.NewWriter(buf)\n\tw.WriteQuads(testData)\n\tw.Close()\n\treturn buf.String()\n}\n\nfunc TestCayleyExport(t *testing.T) {\n\tqs := memstore.New(testData...)\n\tqw, err := graph.NewQuadWriter(\"single\", qs, graph.Options{})\n\trequire.NoError(t, err)\n\th := &graph.Handle{QuadStore: qs, QuadWriter: qw}\n\tchttp.SetupRoutes(h, &chttp.Config{})\n\n\tlis, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tlis.Close()\n\t})\n\n\tsrv := &http.Server{\n\t\tAddr: lis.Addr().String(),\n\t}\n\tgo srv.Serve(lis)\n\n\tcmd := NewCmd()\n\tb := bytes.NewBufferString(\"\")\n\tcmd.SetOut(b)\n\tcmd.SetArgs([]string{\n\t\t\"--uri\",\n\t\tfmt.Sprintf(\"http://%s\", lis.Addr().String()),\n\t})\n\terr = cmd.Execute()\n\trequire.NoError(t, err)\n\tdata := serializeTestData()\n\trequire.NotEmpty(t, data)\n\trequire.Equal(t, data, b.String())\n}\n"
  },
  {
    "path": "cmd/cayleyimport/cayleyimport.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\n\t// Load all supported quad formats.\n\t\"github.com/cayleygraph/quad\"\n\t_ \"github.com/cayleygraph/quad/jsonld\"\n\t_ \"github.com/cayleygraph/quad/nquads\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst defaultFormat = \"jsonld\"\n\n// NewCmd creates the command\nfunc NewCmd() *cobra.Command {\n\tvar quiet bool\n\tvar uri, formatName string\n\n\tvar cmd = &cobra.Command{\n\t\tUse:   \"cayleyimport <file>\",\n\t\tShort: \"Import data into Cayley. If no file is provided, cayleyimport reads from stdin.\",\n\t\tArgs:  cobra.MaximumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif quiet {\n\t\t\t\tclog.SetV(500)\n\t\t\t}\n\t\t\tvar format *quad.Format\n\t\t\tvar reader io.Reader\n\t\t\tif formatName != \"\" {\n\t\t\t\tformat = quad.FormatByName(formatName)\n\t\t\t}\n\t\t\tif len(args) == 0 {\n\t\t\t\tin := cmd.InOrStdin()\n\t\t\t\tif !hasIn(in) {\n\t\t\t\t\treturn errors.New(\"Either provide file to read from or pipe data\")\n\t\t\t\t}\n\t\t\t\treader = in\n\t\t\t} else {\n\t\t\t\tfileName := args[0]\n\t\t\t\tif formatName == \"\" {\n\t\t\t\t\tformat = formatByFileName(fileName)\n\t\t\t\t\tif format == nil {\n\t\t\t\t\t\tclog.Warningf(\"File has unknown extension %v. Defaulting to %v\", fileName, defaultFormat)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfile, err := os.Open(fileName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdefer file.Close()\n\t\t\t\treader = file\n\t\t\t}\n\t\t\tif format == nil {\n\t\t\t\tformat = quad.FormatByName(defaultFormat)\n\t\t\t}\n\t\t\tr, err := http.Post(uri+\"/api/v2/write\", format.Mime[0], reader)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer r.Body.Close()\n\t\t\tbody, err := ioutil.ReadAll(r.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif r.StatusCode == http.StatusOK {\n\t\t\t\tvar response struct {\n\t\t\t\t\tResult string `json:\"result\"`\n\t\t\t\t\tCount  string `json:\"count\"`\n\t\t\t\t\tError  string `json:\"error\"`\n\t\t\t\t}\n\t\t\t\tjson.Unmarshal(body, &response)\n\t\t\t\tif response.Error != \"\" {\n\t\t\t\t\treturn errors.New(response.Error)\n\t\t\t\t}\n\t\t\t\tif !quiet {\n\t\t\t\t\tfmt.Println(response.Result)\n\t\t\t\t}\n\t\t\t} else if r.StatusCode == http.StatusNotFound {\n\t\t\t\treturn errors.New(\"Database instance does not support write\")\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tcmd.Flags().StringVarP(&uri, \"uri\", \"\", \"http://127.0.0.1:64210\", \"Cayley URI connection string\")\n\tcmd.Flags().StringVarP(&formatName, \"format\", \"\", \"\", \"format of the provided data (if can not be detected defaults to JSON-LD)\")\n\tcmd.Flags().BoolVarP(&quiet, \"quiet\", \"q\", false, \"hide all log output\")\n\treturn cmd\n}\n\nfunc main() {\n\tcmd := NewCmd()\n\tif err := cmd.Execute(); err != nil {\n\t\tos.Exit(1)\n\t}\n}\n\nfunc hasIn(in io.Reader) bool {\n\tif in == os.Stdin {\n\t\tstat, _ := os.Stdin.Stat()\n\t\treturn (stat.Mode() & os.ModeCharDevice) == 0\n\t}\n\treturn true\n}\n\nfunc formatByFileName(fileName string) *quad.Format {\n\text := filepath.Ext(fileName)\n\treturn quad.FormatByExt(ext)\n}\n"
  },
  {
    "path": "cmd/cayleyimport/cayleyimport_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"path\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\tchttp \"github.com/cayleygraph/cayley/internal/http\"\n)\n\nvar expectData = []quad.Quad{\n\t{quad.IRI(\"http://example.com/alice\"), quad.IRI(\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\"), quad.IRI(\"http://xmlns.com/foaf/0.1/Person\"), quad.Value(nil)},\n\t{quad.IRI(\"http://example.com/alice\"), quad.IRI(\"http://xmlns.com/foaf/0.1/knows\"), quad.IRI(\"http://example.com/bob\"), nil},\n\t{quad.IRI(\"http://example.com/alice\"), quad.IRI(\"http://xmlns.com/foaf/0.1/name\"), quad.String(\"Alice\"), nil},\n\t{quad.IRI(\"http://example.com/bob\"), quad.IRI(\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\"), quad.IRI(\"http://xmlns.com/foaf/0.1/Person\"), nil},\n\t{quad.IRI(\"http://example.com/bob\"), quad.IRI(\"http://xmlns.com/foaf/0.1/knows\"), quad.IRI(\"http://example.com/alice\"), nil},\n\t{quad.IRI(\"http://example.com/bob\"), quad.IRI(\"http://xmlns.com/foaf/0.1/name\"), quad.String(\"Bob\"), nil},\n}\n\nfunc allQuads(t testing.TB, qs graph.QuadStore) []quad.Quad {\n\tctx := context.Background()\n\tit := qs.QuadsAllIterator().Iterate()\n\tdefer it.Close()\n\tvar out []quad.Quad\n\tfor it.Next(ctx) {\n\t\tref := it.Result()\n\t\tq, err := qs.Quad(ref)\n\t\trequire.NoError(t, err)\n\t\tout = append(out, q)\n\t}\n\trequire.NoError(t, it.Err())\n\tsort.Sort(quad.ByQuadString(out))\n\treturn out\n}\n\nfunc TestCayleyImport(t *testing.T) {\n\tqs := memstore.New()\n\tqw, err := graph.NewQuadWriter(\"single\", qs, graph.Options{})\n\trequire.NoError(t, err)\n\th := &graph.Handle{QuadStore: qs, QuadWriter: qw}\n\tchttp.SetupRoutes(h, &chttp.Config{})\n\n\tlis, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tlis.Close()\n\t})\n\n\tsrv := &http.Server{\n\t\tAddr: lis.Addr().String(),\n\t}\n\tgo srv.Serve(lis)\n\n\tcmd := NewCmd()\n\tb := bytes.NewBufferString(\"\")\n\tcmd.SetOut(b)\n\tfileName := path.Join(\"..\", \"..\", \"data\", \"people.jsonld\")\n\tcmd.SetArgs([]string{\n\t\tfileName,\n\t\t\"--uri\",\n\t\tfmt.Sprintf(\"http://%s\", lis.Addr().String()),\n\t})\n\terr = cmd.Execute()\n\trequire.NoError(t, err)\n\trequire.Empty(t, b.String())\n\tsort.Sort(quad.ByQuadString(expectData))\n\trequire.Equal(t, expectData, allQuads(t, qs))\n}\n"
  },
  {
    "path": "cmd/docgen/docgen.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/doc\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar (\n\tpackageName = flag.String(\"pck\", \"github.com/cayleygraph/cayley/query/gizmo\", \"\")\n\tout         = flag.String(\"o\", \"-\", \"output file\")\n\tin          = flag.String(\"i\", \"\", \"input file\")\n)\n\nconst placeholder = `#AUTOGENERATED#`\n\nfunc main() {\n\tflag.Parse()\n\n\tfset := token.NewFileSet()\n\tpkgs, err := parser.ParseDir(fset, \"./query/gizmo\", nil, parser.ParseComments)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tp := pkgs[filepath.Base(*packageName)]\n\n\tdp := doc.New(p, *packageName, doc.AllDecls)\n\n\tvar w io.Writer = os.Stdout\n\tif fname := *out; fname != \"\" && fname != \"-\" {\n\t\tf, err := os.Create(fname)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer f.Close()\n\t\tw = f\n\t}\n\tvar r io.Reader = strings.NewReader(placeholder)\n\tif fname := *in; fname != \"\" {\n\t\tf, err := os.Open(fname)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer f.Close()\n\t\tr = f\n\t}\n\tsc := bufio.NewScanner(r)\n\tfor sc.Scan() {\n\t\tline := bytes.TrimSpace(sc.Bytes())\n\t\tif bytes.Equal(line, []byte(placeholder)) {\n\t\t\twriteDocs(w, dp)\n\t\t} else {\n\t\t\tw.Write(line)\n\t\t\tw.Write([]byte(\"\\n\"))\n\t\t}\n\t}\n}\n\nfunc writeDocs(w io.Writer, dp *doc.Package) {\n\ttype Type struct {\n\t\tTitle string\n\t\tName  string\n\t}\n\n\tnames := map[string]Type{\n\t\t\"graphObject\": {\n\t\t\tTitle: \"The `graph` object\",\n\t\t\tName:  \"graph\",\n\t\t},\n\t\t\"pathObject\": {\n\t\t\tTitle: \"Path object\",\n\t\t\tName:  \"path\",\n\t\t},\n\t}\n\tfor _, tp := range dp.Types {\n\t\tt, ok := names[tp.Name]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\ts := tp.Doc\n\t\tif i := strings.IndexAny(s, \"\\n\\r\"); i >= 0 {\n\t\t\ts = s[i+1:]\n\t\t}\n\t\ts = strings.TrimSpace(s)\n\t\tfmt.Fprintf(w, \"## %s\\n\\n\", t.Title)\n\t\tfmt.Fprintf(w, \"%s\\n\\n\", funcDocs(s))\n\t\tfor _, m := range tp.Methods {\n\t\t\tif !isExported(m.Name) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tm.Doc = strings.TrimSpace(m.Doc)\n\t\t\tsig := Signature(m)\n\t\t\tfmt.Fprintf(w, \"### `%s.%s%s`\\n\\n%s\\n\\n\", t.Name, m.Name, sig, funcDocs(m.Doc))\n\t\t}\n\t}\n}\n\nvar reSignature = regexp.MustCompile(`Signature:\\s+\\((.+)\\)`)\n\nfunc Signature(m *doc.Func) string {\n\tif reSignature.MatchString(m.Doc) {\n\t\tsub := reSignature.FindStringSubmatch(m.Doc)\n\t\tm.Doc = strings.Replace(m.Doc, sub[0], \"\", 1)\n\t\treturn \"(\" + sub[1] + \")\"\n\t}\n\ttp := m.Decl.Type\n\tif isJsArgs(tp.Params) {\n\t\treturn \"(*)\"\n\t}\n\tvar names []string\n\tfor _, a := range tp.Params.List {\n\t\tfor _, name := range a.Names {\n\t\t\tnames = append(names, name.Name)\n\t\t}\n\t}\n\tbuf := bytes.NewBuffer(nil)\n\tbuf.WriteRune('(')\n\tbuf.WriteString(strings.Join(names, \", \"))\n\tbuf.WriteRune(')')\n\treturn buf.String()\n}\n\nfunc isExported(s string) bool {\n\treturn ast.IsExported(s)\n}\n\nfunc isJsArgs(f *ast.FieldList) bool {\n\tif len(f.List) != 1 {\n\t\treturn false\n\t}\n\tp := f.List[0]\n\tif len(p.Names) != 1 {\n\t\treturn false\n\t}\n\tsel, ok := p.Type.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn sel.Sel.Name == \"FunctionCall\"\n}\n\nvar reScript = regexp.MustCompile(`//\\s*(\\w+)`)\n\nfunc funcDocs(s string) string {\n\tif s == \"\" {\n\t\treturn \"TODO: docs\"\n\t}\n\tbuf := bytes.NewBuffer(nil)\n\tbuf.Grow(len(s))\n\tsc := bufio.NewScanner(strings.NewReader(s))\n\tconst defaultLang = \"\"\n\tvar (\n\t\tinCode bool\n\t\tlang   string\n\t)\n\tfor sc.Scan() {\n\t\tline := sc.Text()\n\t\tif code := strings.HasPrefix(line, \"\\t\"); code {\n\t\t\tif !inCode {\n\t\t\t\tinCode = true\n\t\t\t\tlang = defaultLang\n\t\t\t\tskip := false\n\t\t\t\tif reScript.MatchString(line) {\n\t\t\t\t\tskip = true\n\t\t\t\t\tlang = reScript.FindStringSubmatch(line)[1]\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(\"```\")\n\t\t\t\tbuf.WriteString(lang)\n\t\t\t\tbuf.WriteString(\"\\n\")\n\t\t\t\tif skip {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tline = strings.TrimPrefix(line, \"\\t\")\n\t\t} else if inCode && !code {\n\t\t\tinCode = false\n\t\t\tbuf.WriteString(\"```\\n\")\n\t\t}\n\t\tbuf.WriteString(line)\n\t\tbuf.WriteRune('\\n')\n\t}\n\tif inCode {\n\t\tbuf.WriteString(\"```\\n\")\n\t}\n\treturn buf.String()\n}\n"
  },
  {
    "path": "cmd/download_ui/download_ui.go",
    "content": "package main\n\nimport (\n\t\"archive/zip\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nconst (\n\tversion       = \"v0.8.0\"\n\tfileURL       = \"https://github.com/cayleygraph/web/releases/download/\" + version + \"/web.zip\"\n\tfileName      = \"web.zip\"\n\tdirectoryName = \"ui/web\"\n)\n\nfunc main() {\n\tlog.Printf(\"Downloading %s to %s...\", fileURL, fileName)\n\tif err := DownloadFile(fileName, fileURL); err != nil {\n\t\tpanic(err)\n\t}\n\tlog.Printf(\"Downloaded %s to %s\", fileURL, fileName)\n\n\tlog.Printf(\"Extracting %s to %s...\", fileName, directoryName)\n\terr := Unzip(fileName, directoryName)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tlog.Printf(\"Extracted %s to %s/\", fileName, directoryName)\n\terr = os.Remove(fileName)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tlog.Printf(\"Removed %s\", fileName)\n}\n\n// DownloadFile will download a url to a local file. It's efficient because it will\n// write as it downloads and not load the whole file into memory.\nfunc DownloadFile(filepath string, url string) error {\n\n\t// Get the data\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn fmt.Errorf(\"Received %v status code instead of 200 for %v\", resp.Status, url)\n\t}\n\n\t// Create the file\n\tout, err := os.Create(filepath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\n\t// Write the body to file\n\t_, err = io.Copy(out, resp.Body)\n\treturn err\n}\n\n// Unzip will decompress a zip archive, moving all files and folders\n// within the zip file (parameter 1) to an output directory (parameter 2).\nfunc Unzip(src string, dest string) error {\n\tr, err := zip.OpenReader(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer r.Close()\n\n\tfor _, f := range r.File {\n\n\t\t// Store filename/path for returning and using later on\n\t\tfpath := filepath.Join(dest, f.Name)\n\n\t\t// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE\n\t\tif !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {\n\t\t\treturn fmt.Errorf(\"%s: illegal file path\", fpath)\n\t\t}\n\n\t\tif f.FileInfo().IsDir() {\n\t\t\t// Make Folder\n\t\t\tos.MkdirAll(fpath, 0755)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Make File\n\t\tif err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\toutFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trc, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = io.Copy(outFile, rc)\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Close the file without defer to close before next iteration of loop\n\t\terr = outFile.Close()\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = rc.Close()\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "configurations/default.json",
    "content": "{\n  \"store\": {\n    \"backend\": \"memstore\"\n  }\n}\n"
  },
  {
    "path": "configurations/persisted.json",
    "content": "{\n  \"store\": {\n    \"backend\": \"bolt\",\n    \"address\": \"data/cayley.db\"\n  }\n}\n"
  },
  {
    "path": "data/30kmoviedata_gephi_meta.nq",
    "content": "</film/performance/character> <gephi:inline> \"true\"^^<schema:Boolean> .\n<type> <gephi:inline> \"true\"^^<schema:Boolean> .\n<name> <gephi:inline> \"true\"^^<schema:Boolean> .\n"
  },
  {
    "path": "data/people.jsonld",
    "content": "{\n  \"@context\": {\n    \"ex\": \"http://example.com/\",\n    \"@vocab\": \"http://xmlns.com/foaf/0.1/\"\n  },\n  \"@graph\": [\n    {\n      \"@id\": \"ex:alice\",\n      \"@type\": \"Person\",\n      \"name\": \"Alice\",\n      \"knows\": {\n        \"@id\": \"ex:bob\"\n      }\n    },\n    {\n      \"@id\": \"ex:bob\",\n      \"@type\": \"Person\",\n      \"name\": \"Bob\",\n      \"knows\": {\n        \"@id\": \"ex:alice\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "data/testdata.nq",
    "content": "<alice> <follows> <bob> .\n<bob> <follows> <fred> .\n<bob> <status> \"cool_person\" .\n<dani> <follows> <bob> .\n<charlie> <follows> <bob> .\n<charlie> <follows> <dani> .\n<dani> <follows> <greg> .\n<dani> <status> \"cool_person\" .\n<emily> <follows> <fred> .\n<fred> <follows> <greg> .\n<greg> <status> \"cool_person\" .\n<predicates> <are> <follows> .\n<predicates> <are> <status> .\n<emily> <status> \"smart_person\" <smart_graph> .\n<greg> <status> \"smart_person\" <smart_graph> .\n"
  },
  {
    "path": "data/testdata_multigraph.nq",
    "content": "<alice> <follows> <bob> .\n<bob> <follows> <fred> .\n<bob> <status> \"cool_person\" .\n<charlie> <follows> <bob> .\n<charlie> <follows> <dani> .\n<dani> <follows> <bob> .\n<dani> <follows> <greg> .\n<dani> <status> \"cool_person\" .\n<emily> <follows> <fred> .\n<fred> <follows> <greg> .\n<greg> <status> \"cool_person\" .\n<predicates> <are> <follows> .\n<predicates> <are> <status> .\n<emily> <status> \"smart_person\" <smart_graph> .\n<greg> <status> \"smart_person\" <smart_graph> .\n<fred> <status> \"smart_person\" <other_graph> .\n"
  },
  {
    "path": "docs/3rd-party-apis.md",
    "content": "# 3rd-Party-APIs\n\nVarious 3rd party APIs\n\n* **Clojure**: [https://github.com/wgb/cayley-clj](https://github.com/wgb/cayley-clj)\n* **Javascript/NodeJS**: [https://github.com/lnshi/node-cayley](https://github.com/lnshi/node-cayley), [https://github.com/villadora/cayley.js](https://github.com/villadora/cayley.js)\n* **Ruby**: [https://github.com/reneklacan/cayley-ruby](https://github.com/reneklacan/cayley-ruby)\n* **PHP**: [https://github.com/mcuadros/php-cayley](https://github.com/mcuadros/php-cayley)\n* **Python**: [https://github.com/ziyasal/pyley](https://github.com/ziyasal/pyley)\n* **.NET**: [https://github.com/ziyasal/Cayley.Net](https://github.com/ziyasal/Cayley.Net)\n* **Rust** \\(in early stage\\): [https://github.com/shamansir/cayley-rust](https://github.com/shamansir/cayley-rust)\n* **Haskell**: [https://github.com/MichelBoucey/cayley-client](https://github.com/MichelBoucey/cayley-client)\n\n"
  },
  {
    "path": "docs/GizmoAPI.md.in",
    "content": "# Gizmo API\n![Autogenerated file](https://img.shields.io/badge/file-generated-orange.svg)\n\n#AUTOGENERATED#"
  },
  {
    "path": "docs/README.md",
    "content": "# Cayley Documentation\n\nWelcome to the Cayley Manual! Cayley is an open-source graph database designed for ease of use and storing complex data. The manual introduces key concepts in Cayley, presents the query languages, and provides operational and administrative considerations and procedures as well as comprehensive reference section.\n\n## Introduction\n\n* [Getting Started](getting-started.md)\n* [Installation](installation.md)\n* [Advanced Use](usage/advanced-use.md)\n* [UI Overview](usage/ui-overview.md)\n* [Project Locations](getting-involved/locations.md)\n\n## Reference\n\n* [Glossary](getting-involved/glossary.md)\n* [Gizmo API](query-languages/gizmoapi.md)\n* [GraphQL](query-languages/graphql.md)\n* [MQL](query-languages/mql.md)\n* [HTTP](usage/http.md)\n* [GephiGraphStream](query-languages/gephigraphstream.md)\n\n## Administrators\n\n* [Configuration](configuration.md)\n* [Migration](usage/migration.md)\n* [Usage as Container](deployment/container.md)\n* [Usage in Kubernetes](./k8s/k8s.md)\n\n## Developers\n\n* [Libraries \\(3rd party\\)](usage/3rd-party-apis.md)\n* [Contributing](getting-involved/contributing.md)\n* [Quick Start As Go Library](usage/quickstart-as-lib.md)\n\n"
  },
  {
    "path": "docs/SUMMARY.md",
    "content": "# Table of contents\n\n* [Cayley Documentation](README.md)\n* [Getting Started](getting-started.md)\n* [Install Cayley](installation.md)\n* [Configuration](configuration.md)\n\n## Usage\n\n* [Quickstart as Library](usage/quickstart-as-lib.md)\n* [Advanced Use](usage/advanced-use.md)\n* [HTTP Methods](usage/http.md)\n* [3rd-Party-APIs](usage/3rd-party-apis.md)\n* [UI Overview](usage/ui-overview.md)\n* [Migration](usage/migration.md)\n\n## Query Languages\n\n* [Gizmo API](query-languages/gizmoapi.md)\n* [GraphQL Guide](query-languages/graphql.md)\n* [MQL Guide](query-languages/mql.md)\n* [Gephi GraphStream](query-languages/gephigraphstream.md)\n\n## Getting Involved\n\n* [Glossary of Terms](getting-involved/glossary.md)\n* [Contributing](getting-involved/contributing.md)\n* [TODOs](getting-involved/todo.md)\n* [Locations of parts of Cayley](getting-involved/locations.md)\n\n## Deployment\n\n* [Running in Docker](deployment/container.md)\n* [Running in Kubernetes](deployment/k8s-1.md)\n\n## Tools\n\n* [Convert Linked Data files](tools/convert-linked-data-files.md)\n\n"
  },
  {
    "path": "docs/advanced-use.md",
    "content": "# Advanced Use\n\n## Initialize A Graph\n\nNow that Cayley is downloaded \\(or built\\), let's create our database. `init` is the subcommand to set up a database and the right indices.\n\nYou can set up a full [configuration file](configuration.md) if you'd prefer, but it will also work from the command line.\n\nExamples for each backend can be found in `store.address` format from [config file](configuration.md).\n\nThose two options \\(db and dbpath\\) are always going to be present. If you feel like not repeating yourself, setting up a configuration file for your backend might be something to do now. There's an example file, `cayley_example.yml` in the root directory.\n\nYou can repeat the `--db (-i)` and `--dbpath (-a)` flags from here forward instead of the config flag, but let's assume you created `cayley_overview.yml`\n\nNote: when you specify parameters in the config file the config flags \\(command line arguments\\) are ignored.\n\n## Load Data Into A Graph\n\nAfter the database is initialized we load the data.\n\n```bash\n./cayley load -c cayley_overview.yml -i data/testdata.nq\n```\n\nAnd wait. It will load. If you'd like to watch it load, you can run\n\n```bash\n./cayley load -c cayley_overview.yml -i data/testdata.nq --alsologtostderr=true\n```\n\nAnd watch the log output go by.\n\nIf you plan to import a large dataset into Cayley and try multiple backends, it makes sense to first convert the dataset to Cayley-specific binary format by running:\n\n```bash\n./cayley conv -i dataset.nq.gz -o dataset.pq.gz\n```\n\nThis will minimize parsing overhead on future imports and will compress dataset a bit better.\n\n## Connect a REPL To Your Graph\n\nNow it's loaded. We can use Cayley now to connect to the graph. As you might have guessed, that command is:\n\n```bash\n./cayley repl -c cayley_overview.yml\n```\n\nWhere you'll be given a `cayley>` prompt. It's expecting Gizmo/JS, but that can also be configured with a flag.\n\nNew nodes and links can be added with the following command:\n\n```bash\ncayley> :a subject predicate object label .\n```\n\nRemoving links works similarly:\n\n```bash\ncayley> :d subject predicate object .\n```\n\nThis is great for testing, and ultimately also for scripting, but the real workhorse is the next step.\n\nGo ahead and give it a try:\n\n```text\n// Simple math\ncayley> 2 + 2\n\n// JavaScript syntax\ncayley> x = 2 * 8\ncayley> x\n\n// See all the entities in this small follow graph.\ncayley> graph.Vertex().All()\n\n// See only dani.\ncayley> graph.Vertex(\"<dani>\").All()\n\n// See who dani follows.\ncayley> graph.Vertex(\"<dani>\").Out(\"<follows>\").All()\n```\n\n## Serve Your Graph\n\nJust as before:\n\n```bash\n./cayley http -c cayley_overview.yml\n```\n\nAnd you'll see a message not unlike\n\n```bash\nlistening on :64210, web interface at http://localhost:64210\n```\n\nIf you visit that address \\(often, [http://localhost:64210](http://localhost:64210)\\) you'll see the full web interface and also have a graph ready to serve queries via the [HTTP API](http.md)\n\n### Access from other machines\n\nWhen you want to reach the API or UI from another machine in the network you need to specify the host argument:\n\n```bash\n./cayley http --config=cayley.cfg.overview --host=0.0.0.0:64210\n```\n\nThis makes it listen on all interfaces. You can also give it the specific the IP address you want Cayley to bind to.\n\n**Warning**: for security reasons you might not want to do this on a public accessible machine.\n\n"
  },
  {
    "path": "docs/api/swagger.yml",
    "content": "openapi: \"3.0.0\"\ninfo:\n  description: \"\"\n  version: \"2.1.0\"\n  title: \"Cayley API\"\n  license:\n    name: \"Apache 2.0\"\n    url: \"http://www.apache.org/licenses/LICENSE-2.0.html\"\nservers:\n  - url: \"http://{host}:{port}\"\n    variables:\n      \"host\":\n        default: \"localhost\"\n      \"port\":\n        default: \"64210\"\ntags:\n  - name: \"data\"\n    description: \"Reading and writing data\"\n  - name: \"queries\"\n    description: \"Querying the graph\"\npaths:\n  /api/v2/formats:\n    get:\n      tags:\n        - \"data\"\n      summary: \"Returns a list of supported data formats\"\n      description: \"\"\n      operationId: \"listFormats\"\n      responses:\n        200:\n          description: \"success\"\n          content:\n            \"application/json\":\n              schema:\n                type: \"object\"\n                properties:\n                  id:\n                    description: \"unique name of the format\"\n                    type: \"string\"\n                  read:\n                    description: \"format is supported for loading quads\"\n                    type: \"boolean\"\n                  write:\n                    description: \"format is supported for exporting quads\"\n                    type: \"boolean\"\n                  nodes:\n                    description: \"format can be used to describe nodes\"\n                    type: \"boolean\"\n                  ext:\n                    description: \"typical file extensions for this format\"\n                    type: \"array\"\n                    items:\n                      type: \"string\"\n                  mime:\n                    description: \"typical content types for this format\"\n                    type: \"array\"\n                    items:\n                      type: \"string\"\n                  binary:\n                    description: \"format uses binary encoding\"\n                    type: \"boolean\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/read:\n    get:\n      tags:\n        - \"data\"\n      summary: \"Reads all quads from the database\"\n      description: \"\"\n      operationId: \"readQuads\"\n      parameters:\n        - name: \"format\"\n          in: \"query\"\n          description: \"Data encoder to use for response. Overrides Accept header.\"\n          required: false\n          schema:\n            type: \"string\"\n            enum:\n              - \"nquads\"\n              - \"jsonld\"\n              - \"json\"\n              - \"json-stream\"\n              - \"pquads\"\n              - \"graphviz\"\n              - \"gml\"\n              - \"graphml\"\n            default: \"nquads\"\n        - name: \"sub\"\n          in: \"query\"\n          description: \"Subjects to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"pred\"\n          in: \"query\"\n          description: \"Predicates to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"obj\"\n          in: \"query\"\n          description: \"Objects to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"label\"\n          in: \"query\"\n          description: \"Labels to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"iri\"\n          in: \"query\"\n          description: \"IRI format to use\"\n          required: false\n          schema:\n            type: \"string\"\n            enum: [\"short\", \"full\"]\n      responses:\n        200:\n          description: \"read successful\"\n          content:\n            \"application/n-quads\":\n              schema:\n                $ref: \"#/components/schemas/NQuads\"\n            \"application/ld+json\":\n              schema:\n                $ref: \"#/components/schemas/JSONLD\"\n            \"application/json\":\n              schema:\n                $ref: \"#/components/schemas/JsonQuads\"\n            \"application/x-json-stream\":\n              schema:\n                $ref: \"#/components/schemas/JsonQuadsStream\"\n            \"application/x-protobuf\":\n              schema:\n                $ref: \"#/components/schemas/PQuads\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/write:\n    post:\n      tags:\n        - \"data\"\n      summary: \"Writes quads to the database\"\n      description: \"\"\n      operationId: \"writeQuads\"\n      requestBody:\n        description: \"File in one of formats specified in Content-Type.\"\n        required: true\n        content:\n          \"application/n-quads\":\n            schema:\n              $ref: \"#/components/schemas/NQuads\"\n          \"application/ld+json\":\n            schema:\n              $ref: \"#/components/schemas/JSONLD\"\n          \"application/json\":\n            schema:\n              $ref: \"#/components/schemas/JsonQuads\"\n          \"application/x-json-stream\":\n            schema:\n              $ref: \"#/components/schemas/JsonQuadsStream\"\n          \"application/x-protobuf\":\n            schema:\n              $ref: \"#/components/schemas/PQuads\"\n      parameters:\n        - name: \"format\"\n          in: \"query\"\n          description: \"Data decoder to use for request. Overrides Content-Type.\"\n          required: false\n          schema:\n            type: \"string\"\n      responses:\n        200:\n          description: \"write successful\"\n          content:\n            application/json:\n              schema:\n                type: \"object\"\n                properties:\n                  result:\n                    type: \"string\"\n                    description: \"legacy success message\"\n                  count:\n                    type: \"integer\"\n                    description: \"number of quads received\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/node/delete:\n    post:\n      tags:\n        - \"data\"\n      summary: \"Removes a node add all associated quads\"\n      description: \"\"\n      operationId: \"deleteNode\"\n      requestBody:\n        description: \"File in one of formats specified in Content-Type.\"\n        required: true\n        content:\n          \"application/n-quads\":\n            schema:\n              $ref: \"#/components/schemas/NQuadsNode\"\n          \"application/json\":\n            schema:\n              $ref: \"#/components/schemas/JsonNode\"\n          \"application/x-protobuf\":\n            schema:\n              $ref: \"#/components/schemas/PNode\"\n      parameters:\n        - name: \"format\"\n          in: \"query\"\n          description: \"Data decoder to use for request. Overrides Content-Type.\"\n          required: false\n          schema:\n            type: \"string\"\n      responses:\n        200:\n          description: \"delete successful\"\n          content:\n            application/json:\n              schema:\n                type: \"object\"\n                properties:\n                  result:\n                    type: \"string\"\n                    description: \"legacy success message\"\n                  count:\n                    type: \"integer\"\n                    description: \"number of nodes deleted\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/delete:\n    post:\n      tags:\n        - \"data\"\n      summary: \"Delete quads from the database\"\n      description: \"\"\n      operationId: \"deleteQuads\"\n      requestBody:\n        description: \"File in one of formats specified in Content-Type.\"\n        required: true\n        content:\n          \"application/n-quads\":\n            schema:\n              $ref: \"#/components/schemas/NQuads\"\n          \"application/ld+json\":\n            schema:\n              $ref: \"#/components/schemas/JSONLD\"\n          \"application/json\":\n            schema:\n              $ref: \"#/components/schemas/JsonQuads\"\n          \"application/x-json-stream\":\n            schema:\n              $ref: \"#/components/schemas/JsonQuadsStream\"\n          \"application/x-protobuf\":\n            schema:\n              $ref: \"#/components/schemas/PQuads\"\n      parameters:\n        - name: \"format\"\n          in: \"query\"\n          description: \"Data decoder to use for request. Overrides Content-Type.\"\n          required: false\n          schema:\n            type: \"string\"\n      responses:\n        200:\n          description: \"write successful\"\n          content:\n            application/json:\n              schema:\n                type: \"object\"\n                properties:\n                  result:\n                    type: \"string\"\n                    description: \"legacy success message\"\n                  count:\n                    type: \"integer\"\n                    description: \"number of quads received\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/query:\n    get:\n      tags:\n        - \"queries\"\n      summary: \"Query the graph\"\n      description: \"\"\n      operationId: \"query-get\"\n      parameters:\n        - name: \"lang\"\n          in: \"query\"\n          description: \"Query language to use\"\n          required: true\n          schema:\n            type: \"string\"\n            enum:\n              - \"gizmo\"\n              - \"graphql\"\n              - \"mql\"\n              - \"sexp\"\n        - name: \"qu\"\n          in: \"query\"\n          description: \"Query text\"\n          required: true\n          schema:\n            type: \"string\"\n      responses:\n        200:\n          description: \"query succesful\"\n          content:\n            \"application/json\":\n              schema:\n                $ref: \"#/components/schemas/QueryResult\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n    post:\n      tags:\n        - \"queries\"\n      summary: \"Query the graph\"\n      description: \"\"\n      operationId: \"query\"\n      parameters:\n        - name: \"lang\"\n          in: \"query\"\n          description: \"Query language to use\"\n          required: true\n          schema:\n            type: \"string\"\n            enum:\n              - \"gizmo\"\n              - \"graphql\"\n              - \"mql\"\n              - \"sexp\"\n      requestBody:\n        description: \"Query text\"\n        required: true\n        content:\n          \"*/*\":\n            schema:\n              type: \"string\"\n            examples:\n              gizmo:\n                summary: \"Gizmo: first 10 nodes\"\n                value: \"g.V().getLimit(10)\"\n              graphql:\n                summary: \"GraphQL: first 10 nodes\"\n                value: \"{\\n  nodes(first: 10){\\n    id\\n  }\\n}\"\n      responses:\n        200:\n          description: \"query succesful\"\n          content:\n            \"application/json\":\n              schema:\n                $ref: \"#/components/schemas/QueryResult\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /api/v2/namespace-rules:\n    get:\n      tags:\n        - \"data\"\n      summary: \"Reads all namespace rules from the database\"\n      description: \"\"\n      operationId: \"getNamespaceRules\"\n      responses:\n        200:\n          description: \"Success\"\n          content:\n            \"application/json\":\n              schema:\n                type: \"array\"\n                items:\n                  type: \"object\"\n                  properties:\n                    prefix:\n                      description: \"Prefix of the namespace\"\n                      type: \"string\"\n                    namespace:\n                      description: \"The namespace prefixed\"\n                      type: \"string\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n    post:\n      tags:\n        - \"data\"\n      summary: \"Registers new namespace rule to the database\"\n      description: \"\"\n      operationId: \"registerNamespaceRule\"\n      responses:\n        201:\n          description: \"Success\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /gephi/gs:\n    get:\n      tags:\n        - \"queries\"\n      summary: \"Gephi GraphStream endpoint\"\n      description: \"\"\n      operationId: \"gephiGraphStream\"\n      parameters:\n        - name: \"mode\"\n          in: \"query\"\n          description: \"Streamer mode\"\n          required: false\n          schema:\n            type: \"string\"\n            enum:\n              - \"raw\"\n              - \"nodes\"\n            default: \"raw\"\n        - name: \"limit\"\n          in: \"query\"\n          description: \"Limit the number of nodes or quads\"\n          required: false\n          schema:\n            type: \"integer\"\n        - name: \"sub\"\n          in: \"query\"\n          description: \"Subjects to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"pred\"\n          in: \"query\"\n          description: \"Predicates to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"obj\"\n          in: \"query\"\n          description: \"Objects to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n        - name: \"label\"\n          in: \"query\"\n          description: \"Labels to filter quads by\"\n          required: false\n          schema:\n            type: \"string\"\n      responses:\n        200:\n          description: \"success\"\n          content:\n            \"application/stream+json\":\n              schema:\n                type: \"string\"\n                format: \"binary\"\n                description: \"stream of JSON objects\"\n        default:\n          description: \"Unexpected error\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\ncomponents:\n  schemas:\n    QueryResult:\n      type: object\n      properties:\n        result:\n          type: array\n          nullable: true\n          items:\n            type: object\n    NQuads:\n      type: \"string\"\n      format: \"binary\"\n      example: |\n        <alice> <follows> <bob> .\n        <bob> <follows> <fred> .\n        <bob> <status> \"cool_person\" .\n        <charlie> <follows> <bob> .\n        <charlie> <follows> <dani> .\n        <dani> <follows> <bob> .\n        <dani> <follows> <greg> .\n        <dani> <status> \"cool_person\" .\n        <emily> <follows> <fred> .\n        <fred> <follows> <greg> .\n        <greg> <status> \"cool_person\" .\n        <predicates> <are> <follows> .\n        <predicates> <are> <status> .\n        <emily> <status> \"smart_person\" <smart_graph> .\n        <greg> <status> \"smart_person\" <smart_graph> .\n    NQuadsNode:\n      type: \"string\"\n      format: \"binary\"\n      example: \"<alice>\"\n    JSONLD:\n      type: \"string\"\n      format: \"binary\"\n      example:\n        {\n          \"@context\": \"http://schema.org/\",\n          \"@type\": \"Person\",\n          \"name\": \"Jane Doe\",\n          \"jobTitle\": \"Professor\",\n          \"telephone\": \"(425) 123-4567\",\n          \"url\": \"http://www.janedoe.com\",\n        }\n      externalDocs:\n        url: \"https://json-ld.org\"\n    JsonQuad:\n      type: \"object\"\n      properties:\n        subject:\n          type: \"string\"\n        predicate:\n          type: \"string\"\n        object:\n          type: \"string\"\n        label:\n          type: \"string\"\n    JsonQuads:\n      type: \"array\"\n      items:\n        type: \"object\"\n        properties:\n          subject:\n            type: \"string\"\n          predicate:\n            type: \"string\"\n          object:\n            type: \"string\"\n          label:\n            type: \"string\"\n    JsonNode:\n      type: \"string\"\n    JsonQuadsStream:\n      type: \"string\"\n      format: \"binary\"\n      description: \"stream of JsonQuad objects\"\n    PQuads:\n      type: \"string\"\n      format: \"binary\"\n      description: \"Cayley-specific binary encoding of quads based on protobuf\"\n    PNode:\n      type: \"string\"\n      format: \"binary\"\n      description: \"Cayley-specific binary encoding of node value based on protobuf\"\n    Error:\n      type: \"object\"\n      properties:\n        error:\n          type: \"string\"\n          description: \"error message\"\n"
  },
  {
    "path": "docs/cayleyexport.md",
    "content": "# `cayleyexport`\n\n```\ncayleyexport <file>\n```\n\n## Synopsis\n\nThe `cayleyexport` tool exports content from a Cayley deployment.\n\nSee the [`cayleyimport`](cayleyimport.md) document for more information regarding [`cayleyimport`](cayleyimport.md), which provides the inverse “importing” capability.\n\nRun `cayleyexport` from the system command line, not the Cayley shell.\n\n## Arguments\n\n## Options\n\n### `--help`\n\nReturns information on the options and use of **cayleyexport**.\n\n### `--quiet`\n\nRuns **cayleyexport** in a quiet mode that attempts to limit the amount of output.\n\n### `--uri=<connectionString>`\n\nSpecify a resolvable URI connection string (enclose in quotes) to connect to the Cayley deployment.\n\n```\n--uri \"http://host[:port]\"\n```\n\n### `--format=<format>`\n\nFormat to use for the exported data (if can not be detected defaults to JSON-LD)\n\n### `--out=<filename>`\n\nSpecifies the location and name of a file to export the data to. If you do not specify a file, **cayleyexport** writes data to the standard output (e.g. “stdout”).\n"
  },
  {
    "path": "docs/cayleyimport.md",
    "content": "# `cayleyimport`\n\n```\ncayleyimport <file>\n```\n\n## Synopsis\n\nThe `cayleyimport` tool imports content created by [`cayleyexport`](cayleyexport.md), or potentially, another third-party export tool.\n\nSee the [`cayleyexport`](cayleyexport.md) document for more information regarding [`cayleyexport`](cayleyexport.md), which provides the inverse “exporting” capability.\n\nRun `cayleyimport` from the system command line, not the Cayley shell.\n\n## Arguments\n\n### `file`\n\nSpecifies the location and name of a file containing the data to import. If you do not specify a file, **cayleyimport** reads data from standard input (e.g. “stdin”).\n\n## Options\n\n### `--help`\n\nReturns information on the options and use of **cayleyimport**.\n\n### `--quiet`\n\nRuns **cayleyimport** in a quiet mode that attempts to limit the amount of output.\n\n### `--uri=<connectionString>`\n\nSpecify a resolvable URI connection string (enclose in quotes) to connect to the Cayley deployment.\n\n```\n--uri \"http://host[:port]\"\n```\n\n### `--format=<format>`\n\nFormat of the provided data (if can not be detected defaults to JSON-LD)\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "# Configuration\n\nCayley can be configured using configuration file written in YAML / JSON or by passing flags to the command line. By default. All command line flags take precedence over the configuration file.\n\n* [Recommended Configuration](configuration.md#Recommended-Configuration)\n* [Configuration Options](configuration.md#Configuration-Options)\n  * [Store](configuration.md#Store)\n  * [Per-Store Options](configuration.md#Per-Store-Options)\n  * [Query](configuration.md#Query)\n  * [Load](configuration.md#Load)\n* [Configuration File Location](configuration.md#Configuration-File-Location)\n\n## Recommended Configuration\n\nBy default, Cayley is using the `memstore` store. `memstore` works best for datasets that can fit into the memory of the machine and workloads which doesn't require persistency. For large datasets and/or workloads with require persistency it is recommended to use the `bolt` store.\n\n## Configuration Options\n\n### Store\n\n#### **`store.backend`**\n\n* Type: String\n* Default: `\"memory\"`\n\nDetermines the type of the underlying database. Options include:\n\n* `memstore`: An in-memory store, based on an initial N-Quads file. Loses all changes when the process exits.\n\n**Key-Value backends**\n\n* `btree`: An in-memory store, used mostly to quickly verify KV backend functionality.\n* `leveldb`: A persistent on-disk store backed by [LevelDB](https://github.com/google/leveldb).\n* `bolt`: Stores the graph data on-disk in a [Bolt](https://github.com/boltdb/bolt) file. Uses more disk space and memory than LevelDB for smaller stores, but is often faster to write to and comparable for large ones, with faster average query times.\n\n**NoSQL backends**\n\nSlower, as it incurs network traffic, but multiple Cayley instances can disappear and reconnect at will, across a potentially horizontally-scaled store.\n\n* `mongo`: Stores the graph data and indices in a [MongoDB](https://www.mongodb.com/) instance.\n* `elastic`: Stores the graph data and indices in a [ElasticSearch](https://www.elastic.co/products/elasticsearch) instance.\n* `couch`: Stores the graph data and indices in a [CouchDB](http://couchdb.apache.org/) instance.\n* `pouch`: Stores the graph data and indices in a [PouchDB](https://pouchdb.com/). Requires building with [GopherJS](https://github.com/gopherjs/gopherjs).\n\n**SQL backends**\n\n* `postgres`: Stores the graph data and indices in a [PostgreSQL](https://www.postgresql.org) instance.\n* `cockroach`: Stores the graph data and indices in a [CockroachDB](https://www.cockroachlabs.com/product/cockroachdb/) cluster.\n* `mysql`: Stores the graph data and indices in a [MySQL](https://www.mysql.com/) or [MariaDB](https://mariadb.org/) instance.\n* `sqlite`: Stores the graph data and indices in a [SQLite](https://www.sqlite.org) database.\n\n#### **`store.address`**\n\n* Type: String\n* Default: \"\"\n* Alias: `store.path`\n\nWhere does the database actually live? Dependent on the type of database. For each datastore:\n\n* `memstore`: Path parameter is not supported.\n* `leveldb`: Directory to hold the LevelDB database files.\n* `bolt`: Path to the persistent single Bolt database file.\n* `mongo`: \"hostname:port\" of the desired MongoDB server. More options can be provided in [mgo](https://godoc.org/github.com/globalsign/mgo#Dial) address format.\n* `elastic`: `http://host:port` of the desired ElasticSearch server.\n* `couch`: `http://user:pass@host:port/dbname` of the desired CouchDB server.\n* `postgres`,`cockroach`: `postgres://[username:password@]host[:port]/database-name?sslmode=disable` of the PostgreSQL database and credentials. Sslmode is optional. More option available on [pq](https://godoc.org/github.com/lib/pq) page.\n* `mysql`: `[username:password@]tcp(host[:3306])/database-name` of the MqSQL database and credentials. More option available on [driver](https://github.com/go-sql-driver/mysql#dsn-data-source-name) page.\n* `sqlite`: `filepath` of the SQLite database. More options available on [driver](https://github.com/mattn/go-sqlite3#connection-string) page.\n\n#### **`store.read_only`**\n\n* Type: Boolean\n* Default: false\n\nIf true, disables the ability to write to the database using the HTTP API \\(will return a 400 for any write request\\). Useful for testing or instances that shouldn't change.\n\n#### **`store.options`**\n\n* Type: Object\n\nSee Per-Database Options, below.\n\n### Per-Store Options\n\nThe `store.options` object in the main configuration file contains any of these following options that change the behavior of the datastore.\n\n#### Memory\n\nNo special options.\n\n#### LevelDB\n\n**`write_buffer_mb`**\n\n* Type: Integer\n* Default: 20\n\nThe size in MiB of the LevelDB write cache. Increasing this number allows for more/faster writes before syncing to disk. Default is 20, for large loads, a recommended value is 200+.\n\n**`cache_size_mb`**\n\n* Type: Integer\n* Default: 2\n\nThe size in MiB of the LevelDB block cache. Increasing this number uses more memory to maintain a bigger cache of quad blocks for better performance.\n\n#### Bolt\n\n**`nosync`**\n\n* Type: Boolean\n* Default: false\n\nOptionally disable syncing to disk per transaction. Nosync being true means much faster load times, but without consistency guarantees.\n\n#### Mongo\n\n**`database_name`**\n\n* Type: String\n* Default: \"cayley\"\n\nThe name of the database within MongoDB to connect to. Manages its own collections and indices therein.\n\n#### PostgreSQL\n\nPostgres version 9.5 or greater is required.\n\n**`db_fill_factor`**\n\n* Type: Integer\n* Default: 50\n\nAmount of empty space as a percentage to leave in the database when creating a table and inserting rows. See [PostgreSQL CreateTable](http://www.postgresql.org/docs/current/static/sql-createtable.html).\n\n**`local_optimize`**\n\n* Type: Boolean\n* Default: true\n\nWhether to skip checking quad store size.\n\nConnection pooling options used to configure the Go sql connection. Go defaults will be used when not specified.\n\n**`maxopenconnections`**\n\n* Type: Integer\n* Default: -1.\n\n**`maxidleconnections`**\n\n* Type: Integer\n* Default: -1.\n\n**`connmaxlifetime`**\n\n* Type: String\n* Default: \"\".\n\n#### Per-Replication Options\n\nThe `replication_options` object in the main configuration file contains any of these following options that change the behavior of the replication manager.\n\n### Query\n\n#### **`timeout`**\n\n* Type: Integer or String\n* Default: 30\n\nThe maximum length of time the Javascript runtime should run until cancelling the query and returning a 408 Timeout. When timeout is an integer is is interpreted as seconds, when it is a string it is [parsed](http://golang.org/pkg/time/#ParseDuration) as a Go time.Duration. A negative duration means no limit.\n\n### Load\n\n#### **`load.ignore_missing`**\n\n* Type: Boolean\n* Default: false\n\nOptionally ignore missing quad on delete.\n\n#### **`load.ignore_duplicate`**\n\n* Type: Boolean\n* Default: false\n\nOptionally ignore duplicated quad on add.\n\n#### **`load.batch`**\n\n* Type: Integer\n* Default: 10000\n\nThe number of quads to buffer from a loaded file before writing a block of quads to the database. Larger numbers are good for larger loads.\n\n## Configuration File Location\n\nCayley looks in the following locations for the configuration file \\(named `cayley.yml` or `cayley.json`\\):\n\n* Command line flag\n* The environment variable `$CAYLEY_CFG`\n* Current directory\n* `$HOME/.cayley/`\n* `/etc/`\n\n"
  },
  {
    "path": "docs/container.md",
    "content": "# Container\n\n## Running in Kubernetes\n\nTo run Cayley in K8S check [this docs section](k8s/k8s.md).\n\n## Running in a container\n\nA container exposing the HTTP API of Cayley is available.\n\n### Running with default configuration\n\nContainer is configured to use BoltDB as a backend by default.\n\n```text\ndocker run -p 64210:64210 -d ghcr.io/cayleygraph/cayley\n```\n\nNew database will be available at [http://localhost:64210](http://localhost:64210).\n\n### Custom configuration\n\nTo run the container one must first setup a data directory that contains the configuration file and optionally contains persistent files \\(i.e. a boltdb database file\\).\n\n```text\nmkdir data\ncp cayley_example.yml data/cayley.yml\ncp data/testdata.nq data/my_data.nq\n# initialize and serve database\ndocker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.yml --init -i /data/my_data.nq\n# serve existing database\ndocker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.yml\n```\n\n### Other commands\n\nContainer runs `cayley http` command by default. To run any other Cayley command reset the entry point for container:\n\n```text\ndocker run -v $PWD/data:/data ghcr.io/cayleygraph/cayley --entrypoint=cayley version\n```\n\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "# Contributing\n\n## Community Involvement\n\nJoin our community on [discourse.cayley.io](https://discourse.cayley.io) or other [Locations](locations.md).\n\n## Simply building Cayley\n\nFollow the instructions for running Cayley locally:\n\n```text\n# clone project\ngit clone https://github.com/cayleygraph/cayley\ncd cayley\n\n# Download dependencies\ngo mod download\n\n# Update web files (optional)\n\ngo run cmd/download_ui/download_ui.go\n```\n\n# build the binary\n\ngo build ./cmd/cayley\n\n# try the generated binary\n\n```bash\n./cayley help\n```\n\nGive it a quick test with:\n\n```text\n./cayley repl -i data/testdata.nq\n```\n\nTo run the web frontend, replace the \"repl\" command with \"http\"\n\n```text\n./cayley http -i data/testdata.nq\n```\n\nYou can now open the WebUI in your browser: [http://127.0.0.1:64210](http://127.0.0.1:64210)\n\n## Hacking on Cayley\n\nIf you just want to build Cayley and check out the source, or use it as a library, a simple `go get github.com/cayleygraph/cayley` will work!\n\nBut suppose you want to contribute back on your own fork \\(and pull requests are welcome!\\). A good way to do this is to set up your \\$GOPATH and then...\n\n```text\ngit clone https://github.com/$GITHUBUSERNAME/cayley\n```\n\n...where \\$GITHUBUSERNAME is, well, your GitHub username :\\) You'll probably want to add\n\n```text\ncd cayley\ngit remote add upstream http://github.com/cayleygraph/cayley\n```\n\nSo that you can keep up with the latest changes by periodically running\n\n```text\ngit pull --rebase upstream\n```\n\nWith that in place, that folder will reflect your local fork, be able to take changes from the official fork, and build in the Go style.\n\nFor iterating, it can be helpful to, from the directory, run\n\n```text\ngo build ./cmd/cayley && ./cayley <subcommand> <your options>\n```\n\nWhich will also resolve the relevant static content paths for serving HTTP.\n\n**Reminder:** add yourself to CONTRIBUTORS and AUTHORS.\n\n## Running Unit Tests\n\nFirst, `cd` into the `cayley` project folder and run:\n\n```text\ngo test ./...\n```\n\nIf you have a Docker installed, you can also run tests for remote backend implementations:\n\n```text\ngo test -tags docker ./...\n```\n\nIf you have a Docker installed, you only want to run tests for a specific backend implementations eg. mongodb\n\n```text\ngo test -tags docker ./graph/nosql/mongo\n```\n\nIntegration tests can be enabled with environment variable:\n\n```text\nRUN_INTEGRATION=true go test ./...\n```\n"
  },
  {
    "path": "docs/convert-linked-data-files.md",
    "content": "---\ndescription: >-\n  Linked Data has multiple representations. The Cayley CLI includes a utility to\n  convert Linked Data files from one format to another.\n---\n\n# Convert Linked Data files\n\n## Convert from one format to another\n\n```\n$ cayley convert -i data.jsonld -o data.nquads\n```\n\n`-i` is the input file to be converted. In this example it is a [JSON-LD](https://www.w3.org/TR/json-ld11/) file named `data.jsonld`.\n\n`-o` is the file to be created in the desired format. In this example it is a [N-Quads](https://www.w3.org/TR/n-quads/) file named `data.nquads`.\n\n### Explicitly specify formats\n\nThe formats of the input and output files are detected automatically by the file extension. In case a specific format should be used for input or output use `--load_format` and `--dump_format` respectively.\n\n```text\n$ cayley convet -i data.jsonld -o data --dump_format pquads\n```\n\n`--dump_format` is set to the P-Quads format, a binary format used internally in Cayley.\n\n"
  },
  {
    "path": "docs/deployment/container.md",
    "content": "# Running in Docker\n\n## Running in Kubernetes\n\nTo run Cayley in K8S check [this docs section](k8s-1.md).\n\n## Running in a container\n\nA container exposing the HTTP API of Cayley is available.\n\n### Running with default configuration\n\nContainer is configured to use BoltDB as a backend by default.\n\n```text\ndocker run -p 64210:64210 -d ghcr.io/cayleygraph/cayley\n```\n\nNew database will be available at [http://localhost:64210](http://localhost:64210).\n\n### Custom configuration\n\nTo run the container one must first setup a data directory that contains the configuration file and optionally contains persistent files \\(i.e. a boltdb database file\\).\n\n```text\nmkdir data\ncp cayley_example.yml data/cayley.yml\ncp data/testdata.nq data/my_data.nq\n# initialize and serve database\ndocker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.yml --init -i /data/my_data.nq\n# serve existing database\ndocker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.yml\n```\n\n### Other commands\n\nContainer runs `cayley http` command by default. To run any other Cayley command reset the entry point for container:\n\n```text\ndocker run -v $PWD/data:/data ghcr.io/cayleygraph/cayley --entrypoint=cayley version\n```\n\n"
  },
  {
    "path": "docs/deployment/k8s-1.md",
    "content": "# Running in Kubernetes\n\nMost examples requires Kubernetes 1.5+ and PersistentVolumes are configured.\n\nAfter running scripts namespace `cayley` will be created and service with the same name will be available in cluster. Service is of type `ClusterIP` by default. If you want to expose it, consider changing type to `LoadBalancer`.\n\n## Single instance \\(Bolt\\)\n\nThis is a simplest possible configuration: single Cayley instance with persistent storage, using Bolt as a backend.\n\n```bash\nkubectl create -f ./docs/k8s/cayley-single.yml\n```\n\n## Distributed \\(MongoDB cluster\\)\n\nThis example is based on [thesandlord/mongo-k8s-sidecar](https://github.com/thesandlord/mongo-k8s-sidecar) and runs Cayley on top of 3-node Mongo cluster.\n\n```bash\nkubectl create -f ./docs/k8s/cayley-mongo.yml\n```\n\n## Distributed\n\n_TODO: PostgreSQL, CockroachDB_\n\n"
  },
  {
    "path": "docs/docker-compose/docker-compose.mongo.yml",
    "content": "version: \"2.2\"\nservices:\n  cayley:\n    image: cayleygraph/cayley\n    command: http --db mongo --dbpath mongodb://mongo:27017\n    ports:\n      - 64210:64210\n  mongo:\n    image: mongo\n    ports:\n      - 27017:27017\n"
  },
  {
    "path": "docs/faq.md",
    "content": "# Frequently Asked Questiones\n\nComing Soon! Building the list at [https://discourse.cayley.io/t/faq-frequently-asked-questions/127](https://discourse.cayley.io/t/faq-frequently-asked-questions/127)\n\n"
  },
  {
    "path": "docs/gephigraphstream.md",
    "content": "# Gephi GraphStream\n\nCayley supports graph visualisation in Gephi using GraphStream API.\n\nEnpoint can be accessed by adding URL `http://localhost:64210/gephi/gs` to Gephi GraphStream client.\n\n## Options\n\n### `limit`\n\nDefault: `10000`\n\nSets a maximal number of object that will be streamed. Depending on stream mode this could be either nodes or quads.\n\nValues less than 0 interpreted as \"no limit\".\n\n### `mode`\n\nSets streaming mode. Supported values:\n\n* `raw` \\(default\\) - all or selected quads\n* `nodes` - nodes with properties\n\n#### Raw mode\n\nIn this mode Cayley directly streams selected quads to Gephi.\n\nExample URLs:\n\n`/gephi/gs?mode=raw&pred=<follows>&limit=-1` \\(all quads\\)\n\n`/gephi/gs?mode=raw&sub=<bob>&pred=<follows>,<status>&limit=-1` \\(links from `<bob>` via either `<follows>` or `<status>`\\)\n\nParameters:\n\n* `limit` - maximal number of quads returned\n* `sub`,`pred`,`obj`,`label` - only show quads with specified values of Subject, Predicate, Object or Label\n\nThis mode may be useful to visualize small subgraphs, or graphs without metadata such as types and properties. In case of later, large number of quads will be pointing to nodes describing common types or property names. For this kind of graphs `nodes` mode should be used.\n\n#### Nodes with properties\n\nExample URL: `/gephi/gs?mode=nodes&limit=-1`\n\nIn this mode Cayley streams all nodes and links associated with them, but instead of streaming common quads such as types it will inline them as Gephi properties.\n\nLimit corresponds to a number of nodes returned.\n\nBy default, all predicate will be streamed as in `raw` mode, except well-known predicates and ones with `<gephi:inline> = \"true\"`.\n\nList of well-known predicates includes:\n\n* `<rdf:type>` \\(`<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>`\\)\n* `<rdfs:label>` \\(`<http://www.w3.org/2000/01/rdf-schema#label>`\\)\n* `<schema:name>` \\(`<http://schema.org/name>`\\)\n\nTo add custom predicates, write a special triple to database:\n\n```text\n<myCustomProperty> <gephi:inline> \"true\"^^<schema:Boolean> .\n```\n\nThis allows to partition nodes by type or specific property values.\n\nNote: Only one value per predicate is supported for inlined properties.\n\nBy default nodes will have random positions. To specify an exact position for specific node add `<gephi:x>` and `<gephi:y>` properties:\n\n```text\n<node> <gephi:x> \"10\"^^<schema:Integer>\n<node> <gephi:y> \"-12.3\"^^<schema:Float>\n```\n\n"
  },
  {
    "path": "docs/getting-involved/contributing.md",
    "content": "# Contributing\n\n## Contributing\n\n### Community Involvement\n\nJoin our community on [discourse.cayley.io](https://discourse.cayley.io) or other [Locations](locations.md).\n\n### Simply building Cayley\n\nIf your version of Go &lt; 1.13, you need to run:\n\n```text\nexport GO111MODULE=on\n```\n\nFollow the instructions for running Cayley locally:\n\n```text\n# clone project\ngit clone https://github.com/cayleygraph/cayley\ncd cayley\n\n# Download dependencies\ngo mod download\n\n# Update web files (optional)\n\ngo run cmd/download_ui/download_ui.go\n```\n\n## Generate static files go modules\n\npackr2\n\n## build the binary\n\ngo build ./cmd/cayley\n\n## try the generated binary\n\n```bash\n./cayley help\n```\n\nGive it a quick test with:\n\n```text\n./cayley repl -i data/testdata.nq\n```\n\nTo run the web frontend, replace the \"repl\" command with \"http\"\n\n```text\n./cayley http -i data/testdata.nq\n```\n\nYou can now open the WebUI in your browser: [http://127.0.0.1:64210](http://127.0.0.1:64210)\n\n### Hacking on Cayley\n\nFirst, you'll need Go [\\(version 1.11.x or greater\\)](https://golang.org/doc/install) and a Go workspace. This is outlined by the Go team at [http://golang.org/doc/code.html](http://golang.org/doc/code.html) and is sort of the official way of going about it.\n\nIf your version of Go &lt; 1.13, you need to run:\n\n```text\nexport GO111MODULE=on\n```\n\nIf you just want to build Cayley and check out the source, or use it as a library, a simple `go get github.com/cayleygraph/cayley` will work!\n\nBut suppose you want to contribute back on your own fork \\(and pull requests are welcome!\\). A good way to do this is to set up your $GOPATH and then...\n\n```text\nmkdir -p $GOPATH/src/github.com/cayleygraph\ncd $GOPATH/src/github.com/cayleygraph\ngit clone https://github.com/$GITHUBUSERNAME/cayley\n```\n\n...where $GITHUBUSERNAME is, well, your GitHub username :\\) You'll probably want to add\n\n```text\ncd cayley\ngit remote add upstream http://github.com/cayleygraph/cayley\n```\n\nSo that you can keep up with the latest changes by periodically running\n\n```text\ngit pull --rebase upstream\n```\n\nWith that in place, that folder will reflect your local fork, be able to take changes from the official fork, and build in the Go style.\n\nFor iterating, it can be helpful to, from the directory, run\n\n```text\npackr2 && go build ./cmd/cayley && ./cayley <subcommand> <your options>\n```\n\nWhich will also resolve the relevant static content paths for serving HTTP.\n\n**Reminder:** add yourself to CONTRIBUTORS and AUTHORS.\n\n### Running Unit Tests\n\nIf your version of Go &lt; 1.13, you need to run:\n\n```text\nexport GO111MODULE=on\n```\n\nFirst, `cd` into the `cayley` project folder and run:\n\n```text\ngo test ./...\n```\n\nIf you have a Docker installed, you can also run tests for remote backend implementations:\n\n```text\ngo test -tags docker ./...\n```\n\nIf you have a Docker installed, you only want to run tests for a specific backend implementations eg. mongodb\n\n```text\ngo test -tags docker ./graph/nosql/mongo\n```\n\nIntegration tests can be enabled with environment variable:\n\n```text\nRUN_INTEGRATION=true go test ./...\n```\n\n"
  },
  {
    "path": "docs/getting-involved/glossary.md",
    "content": "# Glossary of Terms\n\n_Note: this definitions in this glossary are sequenced so that they build on each other, one to the next, rather than alphabetically._\n\n## triple\n\n1. a data entity composed of subject-predicate-object, like \"Bob is 35\" or \"Bob knows Fred\". \\(A predicate in traditional grammar...is seen as a property that a subject has or is characterized by.\\) [source](https://en.wikipedia.org/wiki/Triplestore) and \\[source\\]\\([https://en.wikipedia.org/wiki/Predicate\\_\\(grammar\\)\\#Predicates\\_in\\_traditional\\_grammar](https://en.wikipedia.org/wiki/Predicate_%28grammar%29#Predicates_in_traditional_grammar)\\)\n\n## triplestore\n\n1. a purpose-built database for the storage and retrieval of triples... [source](https://en.wikipedia.org/wiki/Triplestore)\n\n## quad\n\n1. where triples have the form `{subject, predicate, object}`, quads would have a form along the lines of `{subject, predicate, object, context}` [source](https://en.wikipedia.org/wiki/Named_graph#Named_graphs_and_quads) \n2. You can add context or extra values to triples that identifies them and makes it easy to define subgraphs, or named properties. [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n3. From [Cayley godoc](https://godoc.org/github.com/cayleygraph/quad#Quad):\n\n   ```go\n   type Quad struct {\n    Subject   Value `json:“subject”`\n    Predicate Value `json:“predicate”`\n    Object    Value `json:“object”`\n    Label     Value `json:“label,omitempty”`\n   }\n   ```\n\n## link\n\n1. Another name for a triple, since it \"links\" any two nodes.\n2. Given the triple `{A, knows, C}` you would say in graph terminology that `A` and `C` are \"vertices\" while `knows` is an \"edge\". You would also say that `A`, `knows`, and `C` are all \"nodes\", and they are \"linked\" to one another by the triple.\n\n## IRI\n\n1. IRI is an RDF Internationalized Resource Identifier. [source](https://godoc.org/github.com/cayleygraph/quad#IRI)\n2. An IRI \\(Internationalized Resource Identifier\\) within an RDF graph is a Unicode string that conforms to the syntax defined in RFC 3987. [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-IRIs)\n3. IRIs are a generalization of URIs that permits a wider range of Unicode characters. Every absolute URI and URL is an IRI, but not every IRI is an URI. [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-IRIs)\n\n## RDF\n\n1. [Resource Description Framework](https://en.wikipedia.org/wiki/Resource_Description_Framework), basically a set of standards defined around quads\n2. An RDF triple consists of three components: \n   1. the subject, which is an IRI or a blank node\n   2. the predicate, which is an IRI\n   3. the object, which is an IRI, a literal or a blank node [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-triples)   \n\n## RDF store, quad store, named graph, semantic graph database\n\n1. ...persisting RDF — storing it — became a thing, and these stores were called triple stores. Next they were called quad stores and included information about context and named graphs, then RDF stores, and most recently they call themselves “semantic graph database.” [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n2. Adding a name to the triple makes a \"quad store\" or named graph. [source](https://en.wikipedia.org/wiki/Triplestore#Related_database_types)\n\n## Cayley\n\n1. Cayley is a quad store that supports multiple storage backends.  It supports multiple query languages for traversing and filtering the named graphs formed by its quads, and it has associated tooling such as a CLI, HTTP server, and so on.\n\n## Gizmo\n\n1. A [Gremlin/TinkerPop](http://tinkerpop.apache.org/)-inspired query language for Cayley.  Looks a lot like JavaScript, the syntax is documented [here](https://github.com/cayleygraph/cayley/blob/master/docs/GizmoAPI.md#graphv).\n\n## g.V\\(\\)\n\n1. For Gremlin/TinkerPop, [g.V\\(\\) returns a list of all the vertices in the graph](http://tinkerpop.apache.org/docs/3.3.3/tutorials/gremlins-anatomy/#_graphtraversalsource)\n2. `.v()` is for \"Vertex\" in Gizmo, and it is used like `pathObject = graph.Vertex([nodeId],[nodeId]...)` \\(see \\[\\[path\\|\\#path\\]\\]\\)\n\n## inbound/outbound predicate\n\n1. Inbound/outbound refers to the direction of a relation via a predicate. In the case of the triple \"A follows B\", \"follows\" is an outbound predicate for `A` and an inbound predicate for `B`.\n\n   In/out predicates can be expressed in a query language, for example using the format `resultSet = subject.out(predicate)` to discover matching `Object`s. In the case of the triple \"A follows B\", `A.out(“follows”)` would return a set of nodes which contains `B`. An excellent example of this sort of query format is given in the Gremlin/TinkerPop homepage example:\n\n   ```javascript\n   What are the names of projects that were created by two friends?\n    g.V().match(\n      as(“a”).out(“knows”).as(“b”),\n      as(“a”).out(“created”).as(“c”),\n      as(“b”).out(“created”).as(“c”),\n      as(“c”).in(“created”).count().is(2)).\n        select(“c”).by(“name”)\n   ```\n\n## direction\n\n1. Direction specifies a node's position within a quad. [source](https://godoc.org/github.com/cayleygraph/quad#Direction)\n\n   ```go\n   const (\n    Any Direction = iota\n    Subject\n    Predicate\n    Object\n    Label\n   )\n   ```\n\n2. Direction is passed to the `Get` method of a quad to access one of its four parts, see [quad.Get\\(d Direction\\) Value](https://godoc.org/github.com/cayleygraph/quad#Quad.Get)\n3. The term \"Direction\" comes about from the concept of traversing a graph. Take for example the triple `{A, follows, B}` and supposing you \"select\" the predicate `follows`. Now you want to traverse the graph, so you move in the `Object` direction, and you now have `B` selected. Whereas the high-level [path](glossary.md#path) abstraction for queries uses inbound/outbound predicates to represent movement on the graph, the bottom-level [iterator](glossary.md#iterator) mechanic uses Direction.\n\n## path\n\n1. Paths are just a set of helpers to build a query, but they are not that good for building something more complex. You can try using [Shapes](glossary.md#shape) for this - it will give you a full control of what the query actually does. [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n2. Path represents either a morphism \\(a pre-defined path stored for later use\\), or a concrete path, consisting of a morphism and an underlying QuadStore. [source](https://godoc.org/github.com/cayleygraph/cayley/query/path#Path)\n3. Underlying code:\n\n   ```go\n   type Path struct {\n      stack       []morphism\n      qs          graph.QuadStore\n      baseContext pathContext\n   }\n\n   type morphism struct {\n      IsTag    bool\n      Reversal func(*pathContext) (morphism, *pathContext)\n      Apply    applyMorphism\n      tags     []string\n   }\n\n   type applyMorphism func(shape.Shape, *pathContext) (shape.Shape, *pathContext)\n   ```\n\n   So, as previously stated, the [path](https://godoc.org/github.com/cayleygraph/cayley/query/path) package is just helper methods on top of the [shape](https://godoc.org/github.com/cayleygraph/cayley/graph/shape) package.\n\n## morphism\n\n1. Morphism is basically a path that is not attached to any particular quadstore or a particular starting point in the graph. Morphisms are meant to be used as a query part that can be applied to other queries to follow a path specified in the Morphism.\n\n   A good example will be a `FollowRecursive` function that will apply a single morphism multiple times to get to all nodes that can be traversed recursively. [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n\n## iterator\n\n1. So a graph query is roughly represented as a tree of iterators – things\n\n   that implement graph.Iterator. An iterator is \\(loosely\\) a stand-in for a\n\n   set of things that match a particular portion of the graph. [source](https://discourse.cayley.io/t/7-7-14-question-about-iterator/62)\n\n## subiterator\n\n1. So a graph query is roughly represented as a tree of iterators...Evaluation is merely calling Next\\(\\) repeatedly on the iterator at the top of the tree. Subiterators, then, are the branches and leaves of the tree. [source](https://discourse.cayley.io/t/7-7-14-question-about-iterator/62)\n2. Example of converting the Cayley-Gremlin-Go-API query `g.V(“B”).In(“follows”).All()` into an iterator tree:\n   * **HasA** \\(subject\\) – gets the things in the subject field for:\n     * **And** – the intersection of:\n       * **LinksTo \\(predicate\\)** links that have the predicate of…:\n         * Fixed iterator containing “follows” – … just the node “follows”.\n       * **LinksTo \\(object\\)** links that have the object field of:\n         * Fixed iterator containing “B” – … just the node “B”\n\n## LinkTo iterator\n\n1. A LinksTo takes a subiterator of nodes, and contains an iteration of links which \"link to\" those nodes in a given direction. ... Can be seen as the dual of the HasA iterator. [source](https://github.com/cayleygraph/cayley/blob/1f53d04893ea9b2736e9b2277bbba3f47b88711a/graph/iterator/linksto.go#L17)\n   * Next\\(\\)ing a LinksTo is straightforward -- iterate through all links to things in the subiterator, and then advance the subiterator, and do it again.\n     * To restate in pseudo-code; `results` is what would be returned in successive `Next()` calls:\n\n       ```go\n       var results []quad.Quad\n       for _, node := range linkTo.subIterator {\n         for _, quad := range allQuads {\n             if quad.Get(linkTo.direction) == node {\n                 results = append(results, quad)\n             }\n         }\n       }\n       ```\n   * Contains\\(\\)ing a LinksTo means, given a link, take the direction we care about and check if it's in our subiterator.\n     * To restate in pseudo-code:\n\n       ```go\n       for _, node := range linkTo.subIterator {\n         if theLink.Get(linkTo.direction) == node {\n             return true\n         }\n       }\n       return false\n       ```\n\n## HasA iterator\n\n1. The HasA takes a subiterator of links, and acts as an iterator of nodes in the given direction. The name comes from the idea that a \"link HasA subject\" or a \"link HasA predicate\". [source](https://github.com/cayleygraph/cayley/blob/41bf496d9dfe622b385c1482789480df8b106472/graph/iterator/hasa.go#L17)\n   * Next\\(\\), [We have a subiterator we can get a value from, and we can take that resultant quad, pull our direction out of it, and return that.](https://github.com/cayleygraph/cayley/blob/41bf496d9dfe622b385c1482789480df8b106472/graph/iterator/hasa.go#L206)\n\n     ```go\n     var results []quad.Value\n     for _, quad := range hasA.subIterator {\n       results = append(results, quad.Get(hasA.direction))\n     }\n     ```\n\n   * Contains\\(\\)\n\n     ```go\n     for _, quad := range hasA.subIterator {\n       if quad.Get(hasA.direction) == theNode {\n           return true\n       }\n     }\n     return false\n     ```\n\n## shape\n\n1. Shape represent a query tree shape. [source](https://godoc.org/github.com/cayleygraph/cayley/graph/shape#Shape)\n\n   ```go\n    type Shape interface {\n        BuildIterator(qs graph.QuadStore) graph.Iterator\n        Optimize(r Optimizer) (Shape, bool)\n    }\n   ```\n\n2. This is the most interesting part of the query system - it describes how exactly the query looks like. ... This package also describes different query optimizations that are not specific to a backend. ... You can write a query using either Paths, Shapes or raw Iterators... [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n3. A Shape seems to be an abstract representation of a query, a level above Iterators and a level below Paths.  You can perform various operations on it \\(traverse inbound/outbound predicates, find unions and intersections, etc.\\) and most importantly build a tree of Iterators from it, which will do the mechanical act of processing quads to find results.\n\n## token\n\n1. In the context of a [quad store](https://godoc.org/github.com/cayleygraph/cayley/graph#QuadStore), a [graph.Value](https://godoc.org/github.com/cayleygraph/cayley/graph#Value). However the backend wishes to implement it, a Value is merely a token to a quad or a node that the backing store itself understands, and the base iterators pass around.\n\n   For example, in a very traditional, graphd-style graph, these are int64s \\(guids of the primitives\\). In a very direct sort of graph, these could be pointers to structs, or merely quads, or whatever works best for the backing store.\n\n## reification\n\n1. “With reification, we create a metagraph on top of our graph that represents the statement that we have here. We create a new node that represents a statement and points at the subject...” [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n2. Reifying a relationship means viewing it as an entity. The purpose of reifying a relationship is to make it explicit, when additional information needs to be added to it.\n\n   Viewing a relationship as an entity, one can say that the entity reifies the relationship. This is called reification of a relationship. Like any other entity, it must be an instance of an entity type. [source](https://en.wikipedia.org/wiki/Reification_)\n\n"
  },
  {
    "path": "docs/getting-involved/locations.md",
    "content": "# Locations of parts of Cayley\n\n## Community\n\n* Where is the beating heart of this community?  [https://discourse.cayley.io/](https://discourse.cayley.io/)\n* Where is the mailing list?  [https://discourse.cayley.io/](https://discourse.cayley.io/) \\(enable mailing list mode under options\\)\n* Where is the chat room? [cayleygraph.slack.com](https://cayleygraph.slack.com) -- Invite [here](https://cayley-slackin.herokuapp.com/)\n* Where should I start contributing? [Contributing.md](contributing.md) \\(Also: [How to get involved!](https://discourse.cayley.io/t/how-to-get-involved/44)\\)\n* Where is the site-content? [https://github.com/cayleygraph/site-content](https://github.com/cayleygraph/site-content)\n* Where are issues? [https://github.com/cayleygraph/cayley/issues](https://github.com/cayleygraph/cayley/issues)\n* Where is the graph source code? [https://github.com/cayleygraph/cayley](https://github.com/cayleygraph/cayley)\n* Where are pull requests? [https://github.com/cayleygraph/cayley/pulls](https://github.com/cayleygraph/cayley/pulls)\n* Where is the wiki?  It was at [https://github.com/cayleygraph/cayley/wiki](https://github.com/cayleygraph/cayley/wiki), but we are deprecating it.\n* Where is the \\(old\\) mailing list?  It was at [https://groups.google.com/forum/\\#!forum/cayley-users](https://groups.google.com/forum/#!forum/cayley-users), but we are deprecating it for this forum \\(and its mailing list mode\\).\n\n"
  },
  {
    "path": "docs/getting-involved/todo.md",
    "content": "# TODOs\n\nThe main source of our TODO list is our [Github Issues](https://github.com/cayleygraph/cayley/issues), so we are going to try to avoid duplicating them here.\n\n## Anything marked \"TODO\" in the code.\n\nUsually something that should be taken care of.\n\n"
  },
  {
    "path": "docs/getting-started.md",
    "content": "# Getting Started\n\nThis guide will take you through starting a graph based on provided data.\n\n## Prerequisites\n\nThis tutorial requires you to be connected to **local Cayley installation**. For more information on installing Cayley locally, see [Install Cayley](installation.md).\n\n## Start Cayley\n\n```bash\ncayley http\n```\n\nYou should see:\n\n```text\nCayley version: 0.7.7 (dev snapshot)\nusing backend \"memstore\"\nlistening on 127.0.0.1:64210, web interface at http://127.0.0.1:64210\n```\n\nYou can now open the web-interface on: [localhost:64210](http://localhost:64210/).\n\nCayley is configured by default to run in memory \\(That's what `backend memstore` means\\). To change the configuration see the documentation for [Configuration File](configuration.md) or run `cayley http --help`.\n\nFor more information about the UI see: [UI Overview](usage/ui-overview.md)\n\n## Run with sample data\n\n### Download sample data\n\n[Sample Data](https://github.com/cayleygraph/cayley/raw/master/data/30kmoviedata.nq.gz)\n\n### Run Cayley\n\n```bash\ncayley http --load 30kmoviedata.nq.gz\n```\n\n## Query Data\n\nUsing the 30kmoviedata.nq dataset from above, let's walk through some simple queries:\n\n### Query all vertices in the graph\n\nTo select all vertices in the graph call, limit to 5 first results. `g` and `V` are synonyms for `graph` and `Vertex` respectively, as they are quite common.\n\n```javascript\ng.V().getLimit(5);\n```\n\n### Match a property of a vertex\n\nFind vertex with property \"Humphrey Bogart\"\n\n```javascript\ng.V()\n  .has(\"<name>\", \"Humphrey Bogart\")\n  .all();\n```\n\nYou may start to notice a pattern here: with Gizmo, the query lines tend to:\n\nStart somewhere in the graph \\| Follow a path \\| Run the query with \"all\" or \"getLimit\"\n\n### Match a complex path\n\nGet the list of actors in the film\n\n```javascript\ng.V()\n  .has(\"<name>\", \"Casablanca\")\n  .out(\"</film/film/starring>\")\n  .out(\"</film/performance/actor>\")\n  .out(\"<name>\")\n  .all();\n```\n\n### Match\n\nThis is starting to get long. Let's use a Morphism, a pre-defined path stored in a variable, as our linkage\n\n```javascript\nvar filmToActor = g\n  .Morphism()\n  .out(\"</film/film/starring>\")\n  .out(\"</film/performance/actor>\");\n\ng.V()\n  .has(\"<name>\", \"Casablanca\")\n  .follow(filmToActor)\n  .out(\"<name>\")\n  .all();\n```\n\nTo learn more about querying see [Gizmo Documentation](query-languages/gizmoapi.md)\n\n"
  },
  {
    "path": "docs/gizmoapi.md",
    "content": "# Gizmo API\n\n![Autogenerated file](https://img.shields.io/badge/file-generated-orange.svg)\n\n## The `graph` object\n\nName: `graph`, Alias: `g`\n\nThis is the only special object in the environment, generates the query objects. Under the hood, they're simple objects that get compiled to a Go iterator tree when executed.\n\n### `graph.addDefaultNamespaces()`\n\nAddDefaultNamespaces register all default namespaces for automatic IRI resolution.\n\n### `graph.addNamespace(pref, ns)`\n\nAddNamespace associates prefix with a given IRI namespace.\n\n### `graph.emit(*)`\n\nEmit adds data programmatically to the JSON result list. Can be any JSON type.\n\n```javascript\ng.emit({ name: \"bob\" }); // push {\"name\":\"bob\"} as a result\n```\n\n### `graph.loadNamespaces()`\n\nLoadNamespaces loads all namespaces saved to graph.\n\n### `graph.M()`\n\nM is a shorthand for Morphism.\n\n### `graph.Morphism()`\n\nMorphism creates a morphism path object. Unqueryable on it's own, defines one end of the path. Saving these to variables with\n\n```javascript\nvar shorterPath = graph\n  .Morphism()\n  .out(\"foo\")\n  .out(\"bar\");\n```\n\nis the common use case. See also: path.follow\\(\\), path.followR\\(\\).\n\n### `graph.IRI(s)`\n\nUri creates an IRI values from a given string.\n\n### `graph.V(*)`\n\nV is a shorthand for Vertex.\n\n### `graph.Vertex([nodeId],[nodeId]...)`\n\nVertex starts a query path at the given vertex/vertices. No ids means \"all vertices\".\n\nArguments:\n\n* `nodeId` \\(Optional\\): A string or list of strings representing the starting vertices.\n\nReturns: Path object\n\n## Path object\n\nBoth `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods. Note that `.Vertex()` returns a query object, which is a subclass of path object.\n\nFor these examples, suppose we have the following graph:\n\n```text\n+-------+                        +------+\n| alice |-----                 ->| fred |<--\n+-------+     \\---->+-------+-/  +------+   \\-+-------+\n              ----->| #bob# |       |         |*emily*|\n+---------+--/  --->+-------+       |         +-------+\n| charlie |    /                    v\n+---------+   /                  +--------+\n  \\---    +--------+             |*#greg#*|\n      \\-->| #dani# |------------>+--------+\n          +--------+\n```\n\nWhere every link is a `<follows>` relationship, and the nodes with an extra `#` in the name have an extra `<status>` link. As in,\n\n```text\n<dani> -- <status> --> \"cool_person\"\n```\n\nPerhaps these are the influencers in our community. So too are extra `*`s in the name -- these are our smart people, according to the `<smart_graph>` label, eg, the quad:\n\n```text\n<greg> <status> \"smart_person\" <smart_graph> .\n```\n\n### `path.all()`\n\nAll executes the query and adds the results, with all tags, as a string-to-string \\(tag to node\\) map in the output set, one for each path that a traversal could take.\n\n### `path.and(path)`\n\nAnd is an alias for Intersect.\n\n### `path.as(tags)`\n\nAs is an alias for Tag.\n\n### `path.back([tag])`\n\nBack returns current path to a set of nodes on a given tag, preserving all constraints.\n\nIf still valid, a path will now consider their vertex to be the same one as the previously tagged one, with the added constraint that it was valid all the way here. Useful for traversing back in queries and taking another route for things that have matched so far.\n\nArguments:\n\n* `tag`: A previous tag in the query to jump back to.\n\nExample:\n\n```javascript\n// Start from all nodes, save them into start, follow any status links,\n// jump back to the starting node, and find who follows them. Return the result.\n// Results are:\n//   {\"id\": \"<alice>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<charlie>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<charlie>\", \"start\": \"<dani>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<fred>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<fred>\", \"start\": \"<greg>\"}\ng.V()\n  .Tag(\"start\")\n  .out(\"<status>\")\n  .back(\"start\")\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.both([predicatePath], [tags])`\n\nBoth follow the predicate in either direction. Same as Out or In.\n\nExample:\n\n```javascript\n// Find all followers/followees of fred. Returns bob, emily and greg\ng.V(\"<fred>\")\n  .both(\"<follows>\")\n  .all();\n```\n\n### `path.count()`\n\nCount returns a number of results and returns it as a value.\n\nExample:\n\n```javascript\n// Save count as a variable\nvar n = g.V().count();\n// Send it as a query result\ng.emit(n);\n```\n\n### `path.difference(path)`\n\nDifference is an alias for Except.\n\n### `path.except(path)`\n\nExcept removes all paths which match query from current path.\n\nIn a set-theoretic sense, this is \\(A - B\\). While `g.V().except(path)` to achieve `U - B = !B` is supported, it's often very slow. Example:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\ncFollows.except(dFollows).all(); // The set (dani) -- what charlie follows that dani does not also follow.\n// Equivalently, g.V(\"<charlie>\").out(\"<follows>\").except(g.V(\"<dani>\").out(\"<follows>\")).all()\n```\n\n### `path.filter(args)`\n\nFilter applies constraints to a set of nodes. Can be used to filter values by range or match strings.\n\n#### `path.filter(regex(expression, includeIRIs))`\n\nFilters by match a regular expression \\([syntax](https://github.com/google/re2/wiki/Syntax)\\). By default works only on literals unless includeEntities is set to `true`.\n\n### `path.follow(path)`\n\nFollow is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path.\n\nStarts as if at the g.M\\(\\) and follows through the morphism path.\n\nExample:\n\n```javascript\nvar friendOfFriend = g\n  .Morphism()\n  .out(\"<follows>\")\n  .out(\"<follows>\");\n// Returns the followed people of who charlie follows -- a simplistic \"friend of my friend\"\n// and whether or not they have a \"cool\" status. Potential for recommending followers abounds.\n// Returns bob and greg\ng.V(\"<charlie>\")\n  .follow(friendOfFriend)\n  .has(\"<status>\", \"cool_person\")\n  .all();\n```\n\n### `path.followR(path)`\n\nFollowR is the same as Follow but follows the chain in the reverse direction. Flips \"In\" and \"Out\" where appropriate, the net result being a virtual predicate followed in the reverse direction.\n\nStarts at the end of the morphism and follows it backwards \\(with appropriate flipped directions\\) to the g.M\\(\\) location.\n\nExample:\n\n```javascript\nvar friendOfFriend = g\n  .Morphism()\n  .out(\"<follows>\")\n  .out(\"<follows>\");\n// Returns the third-tier of influencers -- people who follow people who follow the cool people.\n// Returns charlie (from bob), charlie (from greg), bob and emily\ng.V()\n  .has(\"<status>\", \"cool_person\")\n  .followR(friendOfFriend)\n  .all();\n```\n\n### `path.followRecursive(*)`\n\nFollowRecursive is the same as Follow but follows the chain recursively.\n\nStarts as if at the g.M\\(\\) and follows through the morphism path multiple times, returning all nodes encountered.\n\nExample:\n\n```javascript\nvar friend = g.Morphism().out(\"<follows>\");\n// Returns all people in Charlie's network.\n// Returns bob and dani (from charlie), fred (from bob) and greg (from dani).\ng.V(\"<charlie>\")\n  .followRecursive(friend)\n  .all();\n```\n\n### `path.forEach(callback) or (limit, callback)`\n\nForEach calls callback\\(data\\) for each result, where data is the tag-to-string map as in All case.\n\nArguments:\n\n* `limit` \\(Optional\\): An integer value on the first `limit` paths to process.\n* `callback`: A javascript function of the form `function(data)`\n\nExample:\n\n```javascript\n// Simulate query.all().all()\ngraph.V(\"<alice>\").ForEach(function(d) {\n  g.emit(d);\n});\n```\n\n### `path.getLimit(limit)`\n\nGetLimit is the same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals.\n\n### `path.has(predicate, object)`\n\nHas filters all paths which are, at this point, on the subject for the given predicate and object, but do not follow the path, merely filter the possible paths.\n\nUsually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\n\nArguments:\n\n* `predicate`: A string for a predicate node.\n* `object`: A string for a object node or a set of filters to find it.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob -- results in alice, charlie and dani\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .all();\n// People charlie follows who then follow fred. Results in bob.\ng.V(\"<charlie>\")\n  .out(\"<follows>\")\n  .has(\"<follows>\", \"<fred>\")\n  .all();\n// People with friends who have names sorting lower then \"f\".\ng.V()\n  .has(\"<follows>\", gt(\"<f>\"))\n  .all();\n```\n\n### `path.hasR(*)`\n\nHasR is the same as Has, but sets constraint in reverse direction.\n\n### `path.in([predicatePath], [tags])`\n\nIn is inverse of Out. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects.\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: All predicates pointing into this node\n  * a string: The predicate name to follow into this node\n  * a list of strings: The predicates to follow into this node\n  * a query path object: The target of which is a set of predicates to follow.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the predicate used to the output set.\n  * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n\nExample:\n\n```javascript\n// Find the cool people, bob, dani and greg\ng.V(\"cool_person\")\n  .in(\"<status>\")\n  .all();\n// Find who follows bob, in this case, alice, charlie, and dani\ng.V(\"<bob>\")\n  .in(\"<follows>\")\n  .all();\n// Find who follows the people emily follows, namely, bob and emily\ng.V(\"<emily>\")\n  .out(\"<follows>\")\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.inPredicates()`\n\nInPredicates gets the list of predicates that are pointing in to a node.\n\nExample:\n\n```javascript\n// bob only has \"<follows>\" predicates pointing inward\n// returns \"<follows>\"\ng.V(\"<bob>\")\n  .inPredicates()\n  .all();\n```\n\n### `path.intersect(path)`\n\nIntersect filters all paths by the result of another query path.\n\nThis is essentially a join where, at the stage of each path, a node is shared. Example:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\ncFollows.intersect(dFollows).all();\n// Equivalently, g.V(\"<charlie>\").out(\"<follows>\").And(g.V(\"<dani>\").out(\"<follows>\")).all()\n```\n\n### `path.is(node, [node..])`\n\nFilter all paths to ones which, at this point, are on the given node.\n\nArguments:\n\n* `node`: A string for a node. Can be repeated or a list of strings.\n\nExample:\n\n```javascript\n// Starting from all nodes in the graph, find the paths that follow bob.\n// Results in three paths for bob (from alice, charlie and dani).all()\ng.V()\n  .out(\"<follows>\")\n  .is(\"<bob>\")\n  .all();\n```\n\n### `path.labelContext([labelPath], [tags])`\n\nLabelContext sets \\(or removes\\) the subgraph context to consider in the following traversals. Affects all In\\(\\), Out\\(\\), and Both\\(\\) calls that follow it. The default LabelContext is null \\(all subgraphs\\).\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: In future traversals, consider all edges, regardless of subgraph.\n  * a string: The name of the subgraph to restrict traversals to.\n  * a list of strings: A set of subgraphs to restrict traversals to.\n  * a query path object: The target of which is a set of subgraphs.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the last traversed label to the output set.\n  * a list of strings: Multiple tags to use as keys to save the label used to the output set.\n\nExample:\n\n```javascript\n// Find the status of people Dani follows\ng.V(\"<dani>\")\n  .out(\"<follows>\")\n  .out(\"<status>\")\n  .all();\n// Find only the statuses provided by the smart_graph\ng.V(\"<dani>\")\n  .out(\"<follows>\")\n  .labelContext(\"<smart_graph>\")\n  .out(\"<status>\")\n  .all();\n// Find all people followed by people with statuses in the smart_graph.\ng.V()\n  .labelContext(\"<smart_graph>\")\n  .in(\"<status>\")\n  .labelContext(null)\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.labels()`\n\nLabels gets the list of inbound and outbound quad labels\n\n### `path.limit(limit)`\n\nLimit limits a number of nodes for current path.\n\nArguments:\n\n* `limit`: A number of nodes to limit results to.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .limit(2)\n  .all();\n```\n\n### `path.map(*)`\n\nMap is a alias for ForEach.\n\n### `path.or(path)`\n\nOr is an alias for Union.\n\n### `path.out([predicatePath], [tags])`\n\nOut is the work-a-day way to get between nodes, in the forward direction. Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects.\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: All predicates pointing out from this node\n  * a string: The predicate name to follow out from this node\n  * a list of strings: The predicates to follow out from this node\n  * a query path object: The target of which is a set of predicates to follow.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the predicate used to the output set.\n  * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n\nExample:\n\n```javascript\n// The working set of this is bob and dani\ng.V(\"<charlie>\")\n  .out(\"<follows>\")\n  .all();\n// The working set of this is fred, as alice follows bob and bob follows fred.\ng.V(\"<alice>\")\n  .out(\"<follows>\")\n  .out(\"<follows>\")\n  .all();\n// Finds all things dani points at. Result is bob, greg and cool_person\ng.V(\"<dani>\")\n  .out()\n  .all();\n// Finds all things dani points at on the status linkage.\n// Result is bob, greg and cool_person\ng.V(\"<dani>\")\n  .out([\"<follows>\", \"<status>\"])\n  .all();\n// Finds all things dani points at on the status linkage, given from a separate query path.\n// Result is {\"id\": \"cool_person\", \"pred\": \"<status>\"}\ng.V(\"<dani>\")\n  .out(g.V(\"<status>\"), \"pred\")\n  .all();\n```\n\n### `path.outPredicates()`\n\nOutPredicates gets the list of predicates that are pointing out from a node.\n\nExample:\n\n```javascript\n// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n// returns \"<follows>\", \"<status>\"\ng.V(\"<bob>\")\n  .outPredicates()\n  .all();\n```\n\n### `path.save(predicate, tag)`\n\nSave saves the object of all quads with predicate into tag, without traversal.\n\nArguments:\n\n* `predicate`: A string for a predicate node.\n* `tag`: A string for a tag key to store the object node.\n\nExample:\n\n```javascript\n// Start from dani and bob and save who they follow into \"target\"\n// Returns:\n//   {\"id\" : \"<bob>\", \"target\": \"<fred>\" },\n//   {\"id\" : \"<dani>\", \"target\": \"<bob>\" },\n//   {\"id\" : \"<dani>\", \"target\": \"<greg>\" }\ng.V(\"<dani>\", \"<bob>\")\n  .save(\"<follows>\", \"target\")\n  .all();\n```\n\n### `path.saveInPredicates(tag)`\n\nSaveInPredicates tags the list of predicates that are pointing in to a node.\n\nExample:\n\n```javascript\n// bob only has \"<follows>\" predicates pointing inward\n// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\ng.V(\"<bob>\")\n  .saveInPredicates(\"pred\")\n  .all();\n```\n\n### `path.saveOpt(*)`\n\nSaveOpt is the same as Save, but returns empty tags if predicate does not exists.\n\n### `path.saveOptR(*)`\n\nSaveOptR is the same as SaveOpt, but tags values via reverse predicate.\n\n### `path.saveOutPredicates(tag)`\n\nSaveOutPredicates tags the list of predicates that are pointing out from a node.\n\nExample:\n\n```javascript\n// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\ng.V(\"<bob>\")\n  .saveInPredicates(\"pred\")\n  .all();\n```\n\n### `path.saveR(*)`\n\nSaveR is the same as Save, but tags values via reverse predicate.\n\n### `path.skip(offset)`\n\nSkip skips a number of nodes for current path.\n\nArguments:\n\n* `offset`: A number of nodes to skip.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob, and skip 2 nodes -- results in dani\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .skip(2)\n  .all();\n```\n\n### `path.tag(tags)`\n\nTag saves a list of nodes to a given tag.\n\nIn order to save your work or learn more about how a path got to the end, we have tags. The simplest thing to do is to add a tag anywhere you'd like to put each node in the result set.\n\nArguments:\n\n* `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached \"Tag\"\n\n  Example:\n\n```javascript\n// Start from all nodes, save them into start, follow any status links, and return the result.\n// Results are:\n//   {\"id\": \"cool_person\", \"start\": \"<bob>\"},\n//   {\"id\": \"cool_person\", \"start\": \"<dani>\"},\n//   {\"id\": \"cool_person\", \"start\": \"<greg>\"},\n//   {\"id\": \"smart_person\", \"start\": \"<emily>\"},\n//   {\"id\": \"smart_person\", \"start\": \"<greg>\"}\ng.V()\n  .tag(\"start\")\n  .out(\"<status>\")\n  .all();\n```\n\n### `path.tagArray(*)`\n\nTagArray is the same as ToArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment.\n\nExample:\n\n```javascript\n// bobTags contains an Array of followers of bob (alice, charlie, dani).\nvar bobTags = g\n  .V(\"<bob>\")\n  .tag(\"name\")\n  .in(\"<follows>\")\n  .tagArray();\n// nameValue should be the string \"<bob>\"\nvar nameValue = bobTags[0][\"name\"];\n```\n\n### `path.tagValue()`\n\nTagValue is the same as TagArray, but limited to one result node. Returns a tag-to-string map.\n\n### `path.toArray(*)`\n\nToArray executes a query and returns the results at the end of the query path as an JS array.\n\nExample:\n\n```javascript\n// bobFollowers contains an Array of followers of bob (alice, charlie, dani).\nvar bobFollowers = g\n  .V(\"<bob>\")\n  .in(\"<follows>\")\n  .toArray();\n```\n\n### `path.toValue()`\n\nToValue is the same as ToArray, but limited to one result node.\n\n### `path.union(path)`\n\nUnion returns the combined paths of the two queries.\n\nNotice that it's per-path, not per-node. Once again, if multiple paths reach the same destination, they might have had different ways of getting there \\(and different tags\\). See also: `path.Tag()`\n\nExample:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), dani, bob (from dani), and greg.\ncFollows.union(dFollows).all();\n```\n\n### `path.unique()`\n\nUnique removes duplicate values from the path.\n\n### `path.order()`\n\nOrder returns values from the path in ascending order.\n"
  },
  {
    "path": "docs/glossary.md",
    "content": "# Glossary of Terms\n\n_Note: this definitions in this glossary are sequenced so that they build on each other, one to the next, rather than alphabetically._\n\n## triple\n\n1. a data entity composed of subject-predicate-object, like \"Bob is 35\" or \"Bob knows Fred\". \\(A predicate in traditional grammar...is seen as a property that a subject has or is characterized by.\\) [source](https://en.wikipedia.org/wiki/Triplestore) and \\[source\\]\\([https://en.wikipedia.org/wiki/Predicate\\_\\(grammar\\)\\#Predicates\\_in\\_traditional\\_grammar](https://en.wikipedia.org/wiki/Predicate_%28grammar%29#Predicates_in_traditional_grammar)\\)\n\n## triplestore\n\n1. a purpose-built database for the storage and retrieval of triples... [source](https://en.wikipedia.org/wiki/Triplestore)\n\n## quad\n\n1. where triples have the form `{subject, predicate, object}`, quads would have a form along the lines of `{subject, predicate, object, context}` [source](https://en.wikipedia.org/wiki/Named_graph#Named_graphs_and_quads) \n2. You can add context or extra values to triples that identifies them and makes it easy to define subgraphs, or named properties. [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n3. From [Cayley godoc](https://godoc.org/github.com/cayleygraph/quad#Quad):\n\n   ```go\n   type Quad struct {\n    Subject   Value `json:“subject”`\n    Predicate Value `json:“predicate”`\n    Object    Value `json:“object”`\n    Label     Value `json:“label,omitempty”`\n   }\n   ```\n\n## link\n\n1. Another name for a triple, since it \"links\" any two nodes.\n2. Given the triple `{A, knows, C}` you would say in graph terminology that `A` and `C` are \"vertices\" while `knows` is an \"edge\". You would also say that `A`, `knows`, and `C` are all \"nodes\", and they are \"linked\" to one another by the triple.\n\n## IRI\n\n1. IRI is an RDF Internationalized Resource Identifier. [source](https://godoc.org/github.com/cayleygraph/quad#IRI)\n2. An IRI \\(Internationalized Resource Identifier\\) within an RDF graph is a Unicode string that conforms to the syntax defined in RFC 3987. [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-IRIs)\n3. IRIs are a generalization of URIs that permits a wider range of Unicode characters. Every absolute URI and URL is an IRI, but not every IRI is an URI. [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-IRIs)\n\n## RDF\n\n1. [Resource Description Framework](https://en.wikipedia.org/wiki/Resource_Description_Framework), basically a set of standards defined around quads\n2. An RDF triple consists of three components: \n   1. the subject, which is an IRI or a blank node\n   2. the predicate, which is an IRI\n   3. the object, which is an IRI, a literal or a blank node [source](https://www.w3.org/TR/rdf11-concepts/#h3_section-triples)   \n\n## RDF store, quad store, named graph, semantic graph database\n\n1. ...persisting RDF — storing it — became a thing, and these stores were called triple stores. Next they were called quad stores and included information about context and named graphs, then RDF stores, and most recently they call themselves “semantic graph database.” [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n2. Adding a name to the triple makes a \"quad store\" or named graph. [source](https://en.wikipedia.org/wiki/Triplestore#Related_database_types)\n\n## Cayley\n\n1. Cayley is a quad store that supports multiple storage backends.  It supports multiple query languages for traversing and filtering the named graphs formed by its quads, and it has associated tooling such as a CLI, HTTP server, and so on.\n\n## Gizmo\n\n1. A [Gremlin/TinkerPop](http://tinkerpop.apache.org/)-inspired query language for Cayley.  Looks a lot like JavaScript, the syntax is documented [here](https://github.com/cayleygraph/cayley/blob/master/docs/GizmoAPI.md#graphv).\n\n## g.V\\(\\)\n\n1. For Gremlin/TinkerPop, [g.V\\(\\) returns a list of all the vertices in the graph](http://tinkerpop.apache.org/docs/3.3.3/tutorials/gremlins-anatomy/#_graphtraversalsource)\n2. `.v()` is for \"Vertex\" in Gizmo, and it is used like `pathObject = graph.Vertex([nodeId],[nodeId]...)` \\(see \\[\\[path\\|\\#path\\]\\]\\)\n\n## inbound/outbound predicate\n\n1. Inbound/outbound refers to the direction of a relation via a predicate.  In the case of the triple \"A follows B\", \"follows\" is an outbound predicate for `A` and an inbound predicate for `B`.    \n\n   In/out predicates can be expressed in a query language, for example using the format `resultSet = subject.out(predicate)` to discover matching `Object`s.  In the case of the triple \"A follows B\", `A.out(“follows”)` would return a set of nodes which contains `B`.  An excellent example of this sort of query format is given in the Gremlin/TinkerPop homepage example:\n\n   ```javascript\n   What are the names of projects that were created by two friends?\n    g.V().match(\n      as(“a”).out(“knows”).as(“b”),\n      as(“a”).out(“created”).as(“c”),\n      as(“b”).out(“created”).as(“c”),\n      as(“c”).in(“created”).count().is(2)).\n        select(“c”).by(“name”)\n   ```\n\n## direction\n\n1. Direction specifies a node's position within a quad. [source](https://godoc.org/github.com/cayleygraph/quad#Direction)\n\n   ```go\n   const (\n    Any Direction = iota\n    Subject\n    Predicate\n    Object\n    Label\n   )\n   ```\n\n2. Direction is passed to the `Get` method of a quad to access one of its four parts, see [quad.Get\\(d Direction\\) Value](https://godoc.org/github.com/cayleygraph/quad#Quad.Get)\n3. The term \"Direction\" comes about from the concept of traversing a graph. Take for example the triple `{A, follows, B}` and supposing you \"select\" the predicate `follows`. Now you want to traverse the graph, so you move in the `Object` direction, and you now have `B` selected. Whereas the high-level [path](glossary.md#path) abstraction for queries uses inbound/outbound predicates to represent movement on the graph, the bottom-level [iterator](glossary.md#iterator) mechanic uses Direction.\n\n## path\n\n1. Paths are just a set of helpers to build a query, but they are not that good for building something more complex. You can try using [Shapes](glossary.md#shape) for this - it will give you a full control of what the query actually does. [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n2. Path represents either a morphism \\(a pre-defined path stored for later use\\), or a concrete path, consisting of a morphism and an underlying QuadStore. [source](https://godoc.org/github.com/cayleygraph/cayley/query/path#Path)\n3. Underlying code:\n\n   ```go\n   type Path struct {\n      stack       []morphism\n      qs          graph.QuadStore\n      baseContext pathContext\n   }\n\n   type morphism struct {\n      IsTag    bool\n      Reversal func(*pathContext) (morphism, *pathContext)\n      Apply    applyMorphism\n      tags     []string\n   }\n\n   type applyMorphism func(shape.Shape, *pathContext) (shape.Shape, *pathContext)\n   ```\n\n   So, as previously stated, the [path](https://godoc.org/github.com/cayleygraph/cayley/query/path) package is just helper methods on top of the [shape](https://godoc.org/github.com/cayleygraph/cayley/graph/shape) package.\n\n## morphism\n\n1. Morphism is basically a path that is not attached to any particular quadstore or a particular starting point in the graph. Morphisms are meant to be used as a query part that can be applied to other queries to follow a path specified in the Morphism.  \n\n   A good example will be a `FollowRecursive` function that will apply a single morphism multiple times to get to all nodes that can be traversed recursively. [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n\n## iterator\n\n1. So a graph query is roughly represented as a tree of iterators – things\n\n   that implement graph.Iterator. An iterator is \\(loosely\\) a stand-in for a\n\n   set of things that match a particular portion of the graph. [source](https://discourse.cayley.io/t/7-7-14-question-about-iterator/62)\n\n## subiterator\n\n1. So a graph query is roughly represented as a tree of iterators...Evaluation is merely calling Next\\(\\) repeatedly on the iterator at the top of the tree. Subiterators, then, are the branches and leaves of the tree. [source](https://discourse.cayley.io/t/7-7-14-question-about-iterator/62)\n2. Example of converting the Cayley-Gremlin-Go-API query `g.V(“B”).In(“follows”).All()` into an iterator tree:\n   * **HasA** \\(subject\\) – gets the things in the subject field for:\n     * **And** – the intersection of:\n       * **LinksTo \\(predicate\\)** links that have the predicate of…:\n         * Fixed iterator containing “follows” – … just the node “follows”.\n       * **LinksTo \\(object\\)** links that have the object field of:\n         * Fixed iterator containing “B” – … just the node “B”\n\n## LinkTo iterator\n\n1. A LinksTo takes a subiterator of nodes, and contains an iteration of links which \"link to\" those nodes in a given direction. ... Can be seen as the dual of the HasA iterator. [source](https://github.com/cayleygraph/cayley/blob/1f53d04893ea9b2736e9b2277bbba3f47b88711a/graph/iterator/linksto.go#L17)\n   * Next\\(\\)ing a LinksTo is straightforward -- iterate through all links to things in the subiterator, and then advance the subiterator, and do it again.\n     * To restate in pseudo-code; `results` is what would be returned in successive `Next()` calls:\n\n       ```go\n       var results []quad.Quad\n       for _, node := range linkTo.subIterator {\n         for _, quad := range allQuads {\n             if quad.Get(linkTo.direction) == node {\n                 results = append(results, quad)\n             }\n         }\n       }\n       ```\n   * Contains\\(\\)ing a LinksTo means, given a link, take the direction we care about and check if it's in our subiterator.\n     * To restate in pseudo-code:\n\n       ```go\n       for _, node := range linkTo.subIterator {\n         if theLink.Get(linkTo.direction) == node {\n             return true\n         }\n       }\n       return false\n       ```\n\n## HasA iterator\n\n1. The HasA takes a subiterator of links, and acts as an iterator of nodes in the given direction. The name comes from the idea that a \"link HasA subject\" or a \"link HasA predicate\". [source](https://github.com/cayleygraph/cayley/blob/41bf496d9dfe622b385c1482789480df8b106472/graph/iterator/hasa.go#L17)\n   * Next\\(\\), [We have a subiterator we can get a value from, and we can take that resultant quad, pull our direction out of it, and return that.](https://github.com/cayleygraph/cayley/blob/41bf496d9dfe622b385c1482789480df8b106472/graph/iterator/hasa.go#L206)\n\n     ```go\n     var results []quad.Value\n     for _, quad := range hasA.subIterator {\n       results = append(results, quad.Get(hasA.direction))\n     }\n     ```\n\n   * Contains\\(\\)\n\n     ```go\n     for _, quad := range hasA.subIterator {\n       if quad.Get(hasA.direction) == theNode {\n           return true\n       }\n     }\n     return false\n     ```\n\n## shape\n\n1. Shape represent a query tree shape. [source](https://godoc.org/github.com/cayleygraph/cayley/graph/shape#Shape)\n\n   ```go\n    type Shape interface {\n        BuildIterator(qs graph.QuadStore) graph.Iterator\n        Optimize(ctx context.Context, r Optimizer) (Shape, bool)\n    }\n   ```\n\n2. This is the most interesting part of the query system - it describes how exactly the query looks like. ... This package also describes different query optimizations that are not specific to a backend. ... You can write a query using either Paths, Shapes or raw Iterators... [source](https://discourse.cayley.io/t/a-variety-of-questions/1183/2)\n3. A Shape seems to be an abstract representation of a query, a level above Iterators and a level below Paths.  You can perform various operations on it \\(traverse inbound/outbound predicates, find unions and intersections, etc.\\) and most importantly build a tree of Iterators from it, which will do the mechanical act of processing quads to find results.\n\n## token\n\n1. In the context of a [quad store](https://godoc.org/github.com/cayleygraph/cayley/graph#QuadStore), a [graph.Value](https://godoc.org/github.com/cayleygraph/cayley/graph#Value).  However the backend wishes to implement it, a Value is merely a token to a quad or a node that the backing store itself understands, and the base iterators pass around.    \n\n    For example, in a very traditional, graphd-style graph, these are int64s \\(guids of the primitives\\). In a very direct sort of graph, these could be pointers to structs, or merely quads, or whatever works best for the backing store.\n\n## reification\n\n1. “With reification, we create a metagraph on top of our graph that represents the statement that we have here. We create a new node that represents a statement and points at the subject...” [source](https://neo4j.com/blog/rdf-triple-store-vs-labeled-property-graph-difference/)\n2. Reifying a relationship means viewing it as an entity. The purpose of reifying a relationship is to make it explicit, when additional information needs to be added to it.   \n\n   Viewing a relationship as an entity, one can say that the entity reifies the relationship. This is called reification of a relationship. Like any other entity, it must be an instance of an entity type. [source](https://en.wikipedia.org/wiki/Reification_)\n\n"
  },
  {
    "path": "docs/graphql.md",
    "content": "# GraphQL Guide\n\n**Disclaimer:** Cayley's GraphQL implementation is not strictly a GraphQL, but only a query language with the same syntax and mostly the same rules.\n\nWe will use [this simple dataset](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/data/testdata.nq) for our examples.\n\nEvery query is represented by tree-like structure of nested objects and properties, similar to [MQL](mql.md).\n\n```graphql\n{\n  nodes{\n    id\n  }\n}\n```\n\nThis particular query is equivalent to all nodes in the graph, where `id` is the special field name for the value of the node itself.\n\nFirst root object in traditional GraphQL \\(named `nodes` here\\) represents a method that will be called on the server to get results. In our current implementation this name serves only as a placeholder and will always execute the object search query.\n\nOur example returns the following result:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\"},\n      {\"id\": \"status\"},\n      {\"id\": \"cool_person\"},\n      {\"id\": \"alice\"},\n      {\"id\": \"greg\"},\n      {\"id\": \"emily\"},\n      {\"id\": \"smart_graph\"},\n      {\"id\": \"predicates\"},\n      {\"id\": \"dani\"},\n      {\"id\": \"fred\"},\n      {\"id\": \"smart_person\"},\n      {\"id\": \"charlie\"},\n      {\"id\": \"are\"},\n      {\"id\": \"follows\"}\n    ]\n  }\n}\n```\n\nFirst level of JSON object corresponds to a request itself, thus either `data` field or `errors` will be present.\n\nAny nested objects will correspond to fields defined in query, including top-level name \\(`nodes`\\).\n\n## Limit and pagination\n\nMaximal number of results can be limited using `first` keyword:\n\n```graphql\n{\n  nodes(first: 10){\n    id\n  }\n}\n```\n\nPagination can be done with `offset` keyword:\n\n```graphql\n{\n  nodes(offset: 5, first: 3){\n    id\n  }\n}\n```\n\nThis query returns objects 5-7.\n\n_Note: Values might be sorted differently, depending on what backend is used._\n\n## Properties\n\nPredicates \\(or properties\\) are added to the object to specify additional fields to load:\n\n```graphql\n{\n  nodes{\n    id, status\n  }\n}\n```\n\nResults:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\", \"status\": \"cool_person\"},\n      {\"id\": \"greg\", \"status\": \"cool_person\"},\n      {\"id\": \"dani\", \"status\": \"cool_person\"},\n      {\"id\": \"greg\", \"status\": \"smart_person\"},\n      {\"id\": \"emily\", \"status\": \"smart_person\"}\n    ]\n  }\n}\n```\n\nAll predicates are interpreted as IRIs and can be written in plain text or with angle brackets: `status` and `<status>` are considered equal. Also, well-known namespaces like RDF, RDFS and Schema.org can be written in short form and will be expanded automatically: `schema:name` and `<schema:name>` will be expanded to `<http://schema.org/name>`.\n\nProperties are required to be present by default and can be set to optional with `@opt` or `@optional` directive:\n\n```graphql\n{\n  nodes{\n    id\n    status @opt\n  }\n}\n```\n\nResults:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\", \"status\": \"cool_person\"},\n      {\"id\": \"status\"},\n      {\"id\": \"cool_person\"},\n      {\"id\": \"alice\"},\n      {\"id\": \"greg\", \"status\": [\"cool_person\", \"smart_person\"]},\n      {\"id\": \"emily\", \"status\": \"smart_person\"},\n      {\"id\": \"smart_graph\"},\n      {\"id\": \"predicates\"},\n      {\"id\": \"dani\", \"status\": \"cool_person\"},\n      {\"id\": \"fred\"},\n      {\"id\": \"smart_person\"},\n      {\"id\": \"charlie\"},\n      {\"id\": \"are\"},\n      {\"id\": \"follows\"}\n    ]\n  }\n}\n```\n\n_Note: Since Cayley has no knowledge about property types and schema, it might decide to return a property as a single value for one object and as an array for another object. This behavior will be fixed in future versions._\n\n## Nested objects\n\nObjects and properties can be nested:\n\n```graphql\n{\n  nodes{\n    id\n    follows {\n      id\n    }\n  }\n}\n```\n\nAll operations available on root also works for nested object, for example the limit:\n\n```graphql\n{\n  nodes(first: 10){\n    id\n    follows(first: 1){\n      id\n    }\n  }\n}\n```\n\n## Reversed predicates\n\nAny predicate can be reversed with `@rev` or `@reverse` directive \\(search for \"in\" links instead of \"out\"\\):\n\n```graphql\n{\n  nodes{\n    id\n    followed: <follows> @rev {\n      id\n    }\n  }\n}\n```\n\n## Filters\n\nObjects can be filtered by specific values of properties:\n\n```graphql\n{\n  nodes(id: <bob>, status: \"cool_person\"){\n    id\n  }\n}\n```\n\nOnly exact match is supported for now.\n\nGraphQL names are interpreted as IRIs and string literals are interpreted as strings. Boolean, integer and float value are also supported and will be converted to `schema:Boolean`, `schema:Integer` and `schema:Float` accordingly.\n\n## Labels\n\nAny fields and traversals can be filtered by quad label with `@label` directive:\n\n```graphql\n{\n  nodes{\n    id\n    follows @label(v: <fb>) {\n      id, name\n      follows @label {\n        id, name\n      }\n    }\n  }\n}\n```\n\nLabel will be inherited by child objects. To reset label filter add `@label` directive without parameters.\n\n## Expanding all properties\n\nTo expand all properties of an object, `*` can be used instead of property name:\n\n```graphql\n{\n  nodes{\n    id\n    follows {*}\n  }\n}\n```\n\n## Un-nest objects\n\nThe following query will return objects with `{id: x, status: {name: y}}` structure:\n\n```graphql\n{\n  nodes{\n    id\n    status {\n      name\n    }\n  }\n}\n```\n\nIt is possible to un-nest `status` field object into parent:\n\n```graphql\n{\n  nodes{\n    id\n    status @unnest {\n      status: name\n    }\n  }\n}\n```\n\nResulted objects will have a flat structure: `{id: x, status: y}`.\n\nArrays fields cannot be un-nested. You can still un-nest such fields by providing a limit directive \\(will select the first value from array\\):\n\n```graphql\n{\n  nodes{\n    id\n    statuses(first: 1) @unnest {\n      status: name\n    }\n  }\n}\n```\n\n"
  },
  {
    "path": "docs/gremlinapi.md",
    "content": "# GremlinAPI\n\nCayley Gremlin API was renamed to [Gizmo](gizmoapi.md) to avoid confusion with TinkerPop Gremlin API.\n\n"
  },
  {
    "path": "docs/hacking.md",
    "content": "# HACKING\n\nSee [Contributing.md](contributing.md)\n\n"
  },
  {
    "path": "docs/http.md",
    "content": "# HTTP Methods\n\nThis file covers deprecated v1 HTTP API. All the methods of v2 HTTP API is described in OpenAPI/Swagger [spec](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/docs/api/swagger.yml) and can be viewed by importing `https://raw.githubusercontent.com/cayleygraph/cayley/master/docs/api/swagger.yml` URL into [Swagger Editor](https://editor.swagger.io/) or [Swagger UI demo](http://petstore.swagger.io/).\n\n## Gephi\n\nCayley supports streaming to Gephi via [GraphStream](gephigraphstream.md).\n\n## API v1\n\nUnless otherwise noted, all URIs take a POST command.\n\n### Queries and Results\n\n#### `/api/v1/query/gizmo`\n\nPOST Body: Javascript source code of the query\n\nResponse: JSON results, depending on the query.\n\n#### `/api/v1/query/graphql`\n\nPOST Body: [GraphQL](graphql.md) query\n\nResponse: JSON results, depending on the query.\n\n#### `/api/v1/query/mql`\n\nPOST Body: JSON MQL query\n\nResponse: JSON results, with a query wrapper:\n\n```javascript\n{\n    \"result\": <JSON Result set>\n}\n```\n\nIf the JSON is invalid or an error occurs:\n\n```javascript\n{\n    \"error\": \"Error message\"\n}\n```\n\n### Query Shapes\n\nResult form:\n\n```javascript\n{\n    \"nodes\": [{\n        \"id\" : integer,\n        \"tags\": [\"list of tags from the query\"],\n        \"values\": [\"known values from the query\"],\n        \"is_link_node\": bool,  // Does the node represent the link or the node (the oval shapes)\n        \"is_fixed\": bool  // Is the node a fixed starting point of the query\n    }],\n\n    \"links\": [{\n        \"source\": integer,  // Node ID\n        \"target\": integer,  // Node ID\n        \"link_node\": integer  // Node ID\n    }]\n}\n```\n\n#### `/api/v1/shape/gizmo`\n\nPOST Body: Javascript source code of the query\n\nResponse: JSON description of the last query executed.\n\n#### `/api/v1/shape/mql`\n\nPOST Body: JSON MQL query\n\nResponse: JSON description of the query.\n\n### Write commands\n\nResponses come in the form\n\n200 Success:\n\n```javascript\n{\n    \"result\": \"Success message.\"\n}\n```\n\n400 / 500 Error:\n\n```javascript\n{\n    \"error\": \"Error message.\"\n}\n```\n\n#### `/api/v1/write`\n\nPOST Body: JSON quads\n\n```javascript\n[{\n    \"subject\": \"Subject Node\",\n    \"predicate\": \"Predicate Node\",\n    \"object\": \"Object node\",\n    \"label\": \"Label node\"  // Optional\n}]   // More than one quad allowed.\n```\n\nResponse: JSON response message\n\n#### `/api/v1/write/file/nquad`\n\nPOST Body: Form-encoded body:\n\n* Key: `NQuadFile`, Value: N-Quad file to write.\n\nResponse: JSON response message\n\nExample:\n\n```text\ncurl http://localhost:64210/api/v1/write/file/nquad -F NQuadFile=@30k.n3\n```\n\n#### `/api/v1/delete`\n\nPOST Body: JSON quads\n\n```javascript\n[{\n    \"subject\": \"Subject Node\",\n    \"predicate\": \"Predicate Node\",\n    \"object\": \"Object node\",\n    \"label\": \"Label node\"  // Optional\n}]   // More than one quad allowed.\n```\n\nResponse: JSON response message.\n\n"
  },
  {
    "path": "docs/installation.md",
    "content": "# Install Cayley\n\n## Install Cayley on Ubuntu\n\n```text\nsnap install --edge --devmode cayley\n```\n\n## Install Cayley on macOS\n\n### Install Homebrew\n\nmacOS does not include the Homebrew brew package by default. Install brew using the [official instructions](https://brew.sh/#install)\n\n### Install Cayley\n\n```bash\nbrew install cayley\n```\n\n## Install Cayley with Docker\n\n```bash\ndocker run -p 64210:64210 cayleygraph/cayley\n```\n\nFor more information see [Container Documentation](deployment/container.md)\n\n## Build from Source\n\nSee instructions in [Contributing](getting-involved/contributing.md)\n\n"
  },
  {
    "path": "docs/k8s/README.md",
    "content": "# k8s\n\n"
  },
  {
    "path": "docs/k8s/cayley-mongo.yml",
    "content": "kind: Namespace\napiVersion: v1\nmetadata:\n  name: cayley\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: mongo\n  namespace: cayley\n  labels:\n    name: mongo\nspec:\n  ports:\n  - name: mgo\n    port: 27017\n    targetPort: mgo\n  clusterIP: None\n  selector:\n    role: mongo\n---\napiVersion: apps/v1beta1\nkind: StatefulSet\nmetadata:\n  name: mongo\n  namespace: cayley\nspec:\n  serviceName: \"mongo\"\n  replicas: 3\n  template:\n    metadata:\n      namespace: cayley\n      labels:\n        role: mongo\n        environment: test\n    spec:\n      terminationGracePeriodSeconds: 10\n      containers:\n        - name: mongo\n          image: mongo:3\n          command:\n            - mongod\n            - \"--replSet\"\n            - rs0\n            - \"--smallfiles\"\n            - \"--noprealloc\"\n          ports:\n            - name: mgo\n              containerPort: 27017\n          volumeMounts:\n            - name: mongo-pvc\n              mountPath: /data/db\n        - name: mongo-sidecar\n          image: cvallance/mongo-k8s-sidecar\n          env:\n            - name: MONGO_SIDECAR_POD_LABELS\n              value: \"role=mongo,environment=test\"\n  volumeClaimTemplates:\n  - metadata:\n      name: mongo-pvc\n    spec:\n      accessModes: [ \"ReadWriteOnce\" ]\n      storageClassName: standard\n      resources:\n        requests:\n          storage: 20Gi\n---\nkind: Service\napiVersion: v1\nmetadata:\n  name: cayley\n  namespace: cayley\nspec:\n  selector:\n    app: cayley\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: http\n---\napiVersion: extensions/v1beta1\nkind: Deployment\nmetadata:\n  name: cayley\n  namespace: cayley\nspec:\n  replicas: 1\n  template:\n    metadata:\n      namespace: cayley\n      labels:\n        app: cayley\n    spec:\n      initContainers:\n      - name: cayley-init\n        image: ghcr.io/cayleygraph/cayley:v0.7.8\n        command:\n        - cayley\n        - init\n        - -d=mongo\n        - -a=mongo\n      containers:\n      - name: cayley\n        image: ghcr.io/cayleygraph/cayley:v0.7.8\n        command:\n        - cayley\n        - http\n        - --init # TODO: remove once initContainers works properly\n        - -d=mongo\n        - -a=mongo\n        - --host=:64210\n        ports:\n        - name: http\n          containerPort: 64210"
  },
  {
    "path": "docs/k8s/cayley-single.yml",
    "content": "kind: Namespace\napiVersion: v1\nmetadata:\n  name: cayley\n---\nkind: Service\napiVersion: v1\nmetadata:\n  name: cayley\n  namespace: cayley\nspec:\n  selector:\n    app: cayley\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: http\n---\nkind: PersistentVolumeClaim\napiVersion: v1\nmetadata:\n  name: cayley-pvc\n  namespace: cayley\nspec:\n  accessModes:\n  - ReadWriteOnce\n  resources:\n    requests:\n      storage: 20Gi\n  storageClassName: standard\n---\napiVersion: extensions/v1beta1\nkind: Deployment\nmetadata:\n  name: cayley\n  namespace: cayley\nspec:\n  replicas: 1 # cannot be really scaled because of local backend\n  template:\n    metadata:\n      namespace: cayley\n      labels:\n        app: cayley\n    spec:\n      initContainers:\n      - name: cayley-init\n        image: ghcr.io/cayleygraph/cayley:v0.7.8\n        command:\n        - cayley\n        - init\n        - -c=/etc/cayley.json\n        volumeMounts:\n        - mountPath: /data\n          name: database\n      containers:\n      - name: cayley\n        image: ghcr.io/cayleygraph/cayley:v0.7.8\n        command:\n        - cayley\n        - http\n        - --init # TODO: remove once initContainers is out of beta\n        - -c=/etc/cayley.json\n        - --host=:64210\n        ports:\n        - name: http\n          containerPort: 64210\n        volumeMounts:\n        - mountPath: /data\n          name: database\n      volumes:\n      - name: database\n        persistentVolumeClaim:\n          claimName: cayley-pvc\n"
  },
  {
    "path": "docs/k8s/k8s.md",
    "content": "# Running in Kubernetes\n\nMost examples requires Kubernetes 1.5+ and PersistentVolumes are configured.\n\nAfter running scripts namespace `cayley` will be created and service with the same name will be available in cluster. Service is of type `ClusterIP` by default. If you want to expose it, consider changing type to `LoadBalancer`.\n\n## Single instance \\(Bolt\\)\n\nThis is a simplest possible configuration: single Cayley instance with persistent storage, using Bolt as a backend.\n\n```bash\nkubectl create -f ./docs/k8s/cayley-single.yml\n```\n\n## Distributed \\(MongoDB cluster\\)\n\nThis example is based on [thesandlord/mongo-k8s-sidecar](https://github.com/thesandlord/mongo-k8s-sidecar) and runs Cayley on top of 3-node Mongo cluster.\n\n```bash\nkubectl create -f ./docs/k8s/cayley-mongo.yml\n```\n\n## Distributed\n\n_TODO: PostgreSQL, CockroachDB_\n\n"
  },
  {
    "path": "docs/locations.md",
    "content": "# Locations of parts of Cayley\n\n## Community\n\n* Where is the beating heart of this community?  [https://discourse.cayley.io/](https://discourse.cayley.io/)\n* Where is the mailing list?  [https://discourse.cayley.io/](https://discourse.cayley.io/) \\(enable mailing list mode under options\\)\n* Where is the chat room? [cayleygraph.slack.com](https://cayleygraph.slack.com) -- Invite [here](https://cayley-slackin.herokuapp.com/)\n* Where should I start contributing? [Contributing.md](contributing.md) \\(Also: [How to get involved!](https://discourse.cayley.io/t/how-to-get-involved/44)\\)\n* Where is the site-content? [https://github.com/cayleygraph/site-content](https://github.com/cayleygraph/site-content)\n* Where are issues? [https://github.com/cayleygraph/cayley/issues](https://github.com/cayleygraph/cayley/issues)\n* Where is the graph source code? [https://github.com/cayleygraph/cayley](https://github.com/cayleygraph/cayley)\n* Where are pull requests? [https://github.com/cayleygraph/cayley/pulls](https://github.com/cayleygraph/cayley/pulls)\n* Where is the wiki?  It was at [https://github.com/cayleygraph/cayley/wiki](https://github.com/cayleygraph/cayley/wiki), but we are deprecating it.\n* Where is the \\(old\\) mailing list?  It was at [https://groups.google.com/forum/\\#!forum/cayley-users](https://groups.google.com/forum/#!forum/cayley-users), but we are deprecating it for this forum \\(and its mailing list mode\\).\n\n"
  },
  {
    "path": "docs/migration.md",
    "content": "# Migration\n\n## From Cayley 0.6.1 to 0.7.x\n\nFirst you need to dump all the data from the database via v0.6.1:\n\n```bash\n./cayley-0.6 dump --db <backend> --dbpath <address> --dump_type pquads --dump ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley-0.6 dump --config <config> --dump_type pquads --dump ./data.pq.gz\n```\n\nAnd load the data into new database via v0.7.x \\(`pq` file extension is important\\):\n\n```bash\n./cayley load --init -d <new-backend> -a <new-address> -i ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley load --init -c <new-config> -i ./data.pq.gz\n```\n\n### Dump via text format\n\nAn above guide uses Cayley-specific binary format to avoid encoding and parsing overhead and to compress output file better.\n\nAs an alternative, a standard nquads file format can be used to dump and load data \\(note `nq` extension\\):\n\n```bash\n./cayley-0.6 dump --config <config> --dump ./data.nq.gz\n./cayley load --init -c <new-config> -i ./data.nq.gz\n```\n\n### Bolt, LevelDB, MongoDB\n\nCayley v0.7.0 is still able to read and write databases of these types in old format. It can be accessed by changing backend name from `bolt`/`leveldb`/`mongo` to `bolt1`/`leveldb1`/`mongo1`. Thus, you can use guide for moving data between different backends for v0.7.x \\(see below\\).\n\n**Note:** support for old versions will be dropped starting from v0.7.1.\n\n### SQL\n\nData format for SQL between v0.6.1 and v0.7.x has not changed significantly. Database can be upgraded by executing the following SQL statements:\n\n```sql\nALTER TABLE quads DROP id;\nALTER TABLE nodes ADD refs INT DEFAULT 0 NOT NULL;\nALTER TABLE nodes ALTER COLUMN refs DROP DEFAULT;\nUPDATE nodes SET refs = \n  (SELECT count(1) FROM quads WHERE subject_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE predicate_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE object_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE label_hash = nodes.hash);\n```\n\n## From different backend \\(Cayley 0.7+\\)\n\nFirst you need to dump all the data from old backend \\(`pq` extension is important\\):\n\n```bash\n./cayley dump -d <backend> -a <address> -o ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley dump -c <config> -o ./data.pq.gz\n```\n\nAnd load the data into a new backend and/or database:\n\n```bash\n./cayley load --init -d <new-backend> -a <new-address> -i ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley load --init -c <new-config> -i ./data.pq.gz\n```\n\n### Dump via text format\n\nAn above guide uses Cayley-specific binary format to avoid encoding and parsing overhead and to compress output file better.\n\nAs an alternative, a standard nquads file format can be used to dump and load data \\(note `nq` extension\\):\n\n```bash\n./cayley dump -c <config> -o ./data.nq.gz\n./cayley load --init -c <new-config> -i ./data.nq.gz\n```\n\n"
  },
  {
    "path": "docs/mql.md",
    "content": "# MQL Guide\n\n## General\n\nCayley's MQL implementation is a work-in-progress clone of [Freebase's MQL API](https://developers.google.com/freebase/mql/). At the moment, it supports very basic queries without some of the extended features. It also aims to be database-agnostic, meaning that the schema inference from Freebase does not \\(yet\\) apply.\n\nEvery JSON Object can be thought of as a node in the graph, and wrapping an object in a list means there may be several of these, or it may be repeated. A simple query like:\n\n```javascript\n[{\n  \"id\": null\n}]\n```\n\nIs equivalent to all nodes in the graph, where \"id\" is the special keyword for the value of the node.\n\nPredicates are added to the object to specify constraints.\n\n```javascript\n[{\n  \"id\": null,\n  \"some_predicate\": \"some value\"\n}]\n```\n\nPredicates can take as values objects or lists of objects \\(subqueries\\), strings and numbers \\(literal IDs that must match -- equivalent to the object {\"id\": \"value\"}\\) or null, which indicates that, while the object must have a predicate that matches, the matching values will replace the null. A single null is one such value, an empty list will be filled with all such values, as strings.\n\n## Keywords\n\n* `id`: The value of the node.\n\n## Reverse Predicates\n\nPredicates always assume a forward direction. That is,\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"some_predicate\": \"B\"\n}]\n```\n\nwill only match if the quad\n\n```text\nA some_predicate B .\n```\n\nexists. In order to reverse the directions, \"!predicates\" are used. So that:\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"!some_predicate\": \"B\"\n}]\n```\n\nwill only match if the quad\n\n```text\nB some_predicate A .\n```\n\nexists.\n\n## Multiple Predicates\n\nJSON does not specify the behavior of objects with the same key. In order to have separate constraints for the same predicate, the prefix \"@name:\" can be applied to any predicate. This is slightly different from traditional MQL in that fully-qualified http paths may be common predicates, so we have an \"@name:\" prefix instead.\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"@x:some_predicate\": \"B\",\n  \"@y:some_predicate\": \"C\"\n\n}]\n```\n\nWill only match if _both_\n\n```text\nA some_predicate B .\nA some_predicate C .\n```\n\nexist.\n\nThis combines with the reversal rule to create paths like `\"@a:!some_predicate\"`\n\n"
  },
  {
    "path": "docs/query-languages/gephigraphstream.md",
    "content": "# Gephi GraphStream\n\nCayley supports graph visualisation in Gephi using GraphStream API.\n\nEnpoint can be accessed by adding URL `http://localhost:64210/gephi/gs` to Gephi GraphStream client.\n\n## Options\n\n### `limit`\n\nDefault: `10000`\n\nSets a maximal number of object that will be streamed. Depending on stream mode this could be either nodes or quads.\n\nValues less than 0 interpreted as \"no limit\".\n\n### `mode`\n\nSets streaming mode. Supported values:\n\n* `raw` \\(default\\) - all or selected quads\n* `nodes` - nodes with properties\n\n#### Raw mode\n\nIn this mode Cayley directly streams selected quads to Gephi.\n\nExample URLs:\n\n`/gephi/gs?mode=raw&pred=<follows>&limit=-1` \\(all quads\\)\n\n`/gephi/gs?mode=raw&sub=<bob>&pred=<follows>,<status>&limit=-1` \\(links from `<bob>` via either `<follows>` or `<status>`\\)\n\nParameters:\n\n* `limit` - maximal number of quads returned\n* `sub`,`pred`,`obj`,`label` - only show quads with specified values of Subject, Predicate, Object or Label\n\nThis mode may be useful to visualize small subgraphs, or graphs without metadata such as types and properties. In case of later, large number of quads will be pointing to nodes describing common types or property names. For this kind of graphs `nodes` mode should be used.\n\n#### Nodes with properties\n\nExample URL: `/gephi/gs?mode=nodes&limit=-1`\n\nIn this mode Cayley streams all nodes and links associated with them, but instead of streaming common quads such as types it will inline them as Gephi properties.\n\nLimit corresponds to a number of nodes returned.\n\nBy default, all predicate will be streamed as in `raw` mode, except well-known predicates and ones with `<gephi:inline> = \"true\"`.\n\nList of well-known predicates includes:\n\n* `<rdf:type>` \\(`<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>`\\)\n* `<rdfs:label>` \\(`<http://www.w3.org/2000/01/rdf-schema#label>`\\)\n* `<schema:name>` \\(`<http://schema.org/name>`\\)\n\nTo add custom predicates, write a special triple to database:\n\n```text\n<myCustomProperty> <gephi:inline> \"true\"^^<schema:Boolean> .\n```\n\nThis allows to partition nodes by type or specific property values.\n\nNote: Only one value per predicate is supported for inlined properties.\n\nBy default nodes will have random positions. To specify an exact position for specific node add `<gephi:x>` and `<gephi:y>` properties:\n\n```text\n<node> <gephi:x> \"10\"^^<schema:Integer>\n<node> <gephi:y> \"-12.3\"^^<schema:Float>\n```\n\n"
  },
  {
    "path": "docs/query-languages/gizmoapi.md",
    "content": "# Gizmo API\n\n![Autogenerated file](https://img.shields.io/badge/file-generated-orange.svg)\n\n## The `graph` object\n\nName: `graph`, Alias: `g`\n\nThis is the only special object in the environment, generates the query objects. Under the hood, they're simple objects that get compiled to a Go iterator tree when executed.\n\n### `graph.addDefaultNamespaces()`\n\nAddDefaultNamespaces register all default namespaces for automatic IRI resolution.\n\n### `graph.addNamespace(pref, ns)`\n\nAddNamespace associates prefix with a given IRI namespace.\n\n### `graph.emit(*)`\n\nEmit adds data programmatically to the JSON result list. Can be any JSON type.\n\n```javascript\ng.emit({ name: \"bob\" }); // push {\"name\":\"bob\"} as a result\n```\n\n### `graph.loadNamespaces()`\n\nLoadNamespaces loads all namespaces saved to graph.\n\n### `graph.M()`\n\nM is a shorthand for Morphism.\n\n### `graph.Morphism()`\n\nMorphism creates a morphism path object. Unqueryable on it's own, defines one end of the path. Saving these to variables with\n\n```javascript\nvar shorterPath = graph\n  .Morphism()\n  .out(\"foo\")\n  .out(\"bar\");\n```\n\nis the common use case. See also: path.follow\\(\\), path.followR\\(\\).\n\n### `graph.IRI(s)`\n\nUri creates an IRI values from a given string.\n\n### `graph.V(*)`\n\nV is a shorthand for Vertex.\n\n### `graph.Vertex([nodeId],[nodeId]...)`\n\nVertex starts a query path at the given vertex/vertices. No ids means \"all vertices\".\n\nArguments:\n\n* `nodeId` \\(Optional\\): A string or list of strings representing the starting vertices.\n\nReturns: Path object\n\n## Path object\n\nBoth `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods. Note that `.Vertex()` returns a query object, which is a subclass of path object.\n\nFor these examples, suppose we have the following graph:\n\n```text\n+-------+                        +------+\n| alice |-----                 ->| fred |<--\n+-------+     \\---->+-------+-/  +------+   \\-+-------+\n              ----->| #bob# |       |         |*emily*|\n+---------+--/  --->+-------+       |         +-------+\n| charlie |    /                    v\n+---------+   /                  +--------+\n  \\---    +--------+             |*#greg#*|\n      \\-->| #dani# |------------>+--------+\n          +--------+\n```\n\nWhere every link is a `<follows>` relationship, and the nodes with an extra `#` in the name have an extra `<status>` link. As in,\n\n```text\n<dani> -- <status> --> \"cool_person\"\n```\n\nPerhaps these are the influencers in our community. So too are extra `*`s in the name -- these are our smart people, according to the `<smart_graph>` label, eg, the quad:\n\n```text\n<greg> <status> \"smart_person\" <smart_graph> .\n```\n\n### `path.all()`\n\nAll executes the query and adds the results, with all tags, as a string-to-string \\(tag to node\\) map in the output set, one for each path that a traversal could take.\n\n### `path.and(path)`\n\nAnd is an alias for Intersect.\n\n### `path.as(tags)`\n\nAs is an alias for Tag.\n\n### `path.back([tag])`\n\nBack returns current path to a set of nodes on a given tag, preserving all constraints.\n\nIf still valid, a path will now consider their vertex to be the same one as the previously tagged one, with the added constraint that it was valid all the way here. Useful for traversing back in queries and taking another route for things that have matched so far.\n\nArguments:\n\n* `tag`: A previous tag in the query to jump back to.\n\nExample:\n\n```javascript\n// Start from all nodes, save them into start, follow any status links,\n// jump back to the starting node, and find who follows them. Return the result.\n// Results are:\n//   {\"id\": \"<alice>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<charlie>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<charlie>\", \"start\": \"<dani>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<bob>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<fred>\", \"start\": \"<greg>\"},\n//   {\"id\": \"<fred>\", \"start\": \"<greg>\"}\ng.V()\n  .Tag(\"start\")\n  .out(\"<status>\")\n  .back(\"start\")\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.both([predicatePath], [tags])`\n\nBoth follow the predicate in either direction. Same as Out or In.\n\nExample:\n\n```javascript\n// Find all followers/followees of fred. Returns bob, emily and greg\ng.V(\"<fred>\")\n  .both(\"<follows>\")\n  .all();\n```\n\n### `path.count()`\n\nCount returns a number of results and returns it as a value.\n\nExample:\n\n```javascript\n// Save count as a variable\nvar n = g.V().count();\n// Send it as a query result\ng.emit(n);\n```\n\n### `path.difference(path)`\n\nDifference is an alias for Except.\n\n### `path.except(path)`\n\nExcept removes all paths which match query from current path.\n\nIn a set-theoretic sense, this is \\(A - B\\). While `g.V().except(path)` to achieve `U - B = !B` is supported, it's often very slow. Example:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\ncFollows.except(dFollows).all(); // The set (dani) -- what charlie follows that dani does not also follow.\n// Equivalently, g.V(\"<charlie>\").out(\"<follows>\").except(g.V(\"<dani>\").out(\"<follows>\")).all()\n```\n\n### `path.filter(args)`\n\nFilter applies constraints to a set of nodes. Can be used to filter values by range or match strings.\n\n#### `path.filter(regex(expression, includeIRIs))`\n\nFilters by match a regular expression \\([syntax](https://github.com/google/re2/wiki/Syntax)\\). By default works only on literals unless includeEntities is set to `true`.\n\n### `path.follow(path)`\n\nFollow is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path.\n\nStarts as if at the g.M\\(\\) and follows through the morphism path.\n\nExample:\n\n```javascript\nvar friendOfFriend = g\n  .Morphism()\n  .out(\"<follows>\")\n  .out(\"<follows>\");\n// Returns the followed people of who charlie follows -- a simplistic \"friend of my friend\"\n// and whether or not they have a \"cool\" status. Potential for recommending followers abounds.\n// Returns bob and greg\ng.V(\"<charlie>\")\n  .follow(friendOfFriend)\n  .has(\"<status>\", \"cool_person\")\n  .all();\n```\n\n### `path.followR(path)`\n\nFollowR is the same as Follow but follows the chain in the reverse direction. Flips \"In\" and \"Out\" where appropriate, the net result being a virtual predicate followed in the reverse direction.\n\nStarts at the end of the morphism and follows it backwards \\(with appropriate flipped directions\\) to the g.M\\(\\) location.\n\nExample:\n\n```javascript\nvar friendOfFriend = g\n  .Morphism()\n  .out(\"<follows>\")\n  .out(\"<follows>\");\n// Returns the third-tier of influencers -- people who follow people who follow the cool people.\n// Returns charlie (from bob), charlie (from greg), bob and emily\ng.V()\n  .has(\"<status>\", \"cool_person\")\n  .followR(friendOfFriend)\n  .all();\n```\n\n### `path.followRecursive(*)`\n\nFollowRecursive is the same as Follow but follows the chain recursively.\n\nStarts as if at the g.M\\(\\) and follows through the morphism path multiple times, returning all nodes encountered.\n\nExample:\n\n```javascript\nvar friend = g.Morphism().out(\"<follows>\");\n// Returns all people in Charlie's network.\n// Returns bob and dani (from charlie), fred (from bob) and greg (from dani).\ng.V(\"<charlie>\")\n  .followRecursive(friend)\n  .all();\n```\n\n### `path.forEach(callback) or (limit, callback)`\n\nForEach calls callback\\(data\\) for each result, where data is the tag-to-string map as in All case.\n\nArguments:\n\n* `limit` \\(Optional\\): An integer value on the first `limit` paths to process.\n* `callback`: A javascript function of the form `function(data)`\n\nExample:\n\n```javascript\n// Simulate query.all().all()\ngraph.V(\"<alice>\").ForEach(function(d) {\n  g.emit(d);\n});\n```\n\n### `path.getLimit(limit)`\n\nGetLimit is the same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals.\n\n### `path.has(predicate, object)`\n\nHas filters all paths which are, at this point, on the subject for the given predicate and object, but do not follow the path, merely filter the possible paths.\n\nUsually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\n\nArguments:\n\n* `predicate`: A string for a predicate node.\n* `object`: A string for a object node or a set of filters to find it.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob -- results in alice, charlie and dani\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .all();\n// People charlie follows who then follow fred. Results in bob.\ng.V(\"<charlie>\")\n  .out(\"<follows>\")\n  .has(\"<follows>\", \"<fred>\")\n  .all();\n// People with friends who have names sorting lower then \"f\".\ng.V()\n  .has(\"<follows>\", gt(\"<f>\"))\n  .all();\n```\n\n### `path.hasR(*)`\n\nHasR is the same as Has, but sets constraint in reverse direction.\n\n### `path.in([predicatePath], [tags])`\n\nIn is inverse of Out. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects.\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: All predicates pointing into this node\n  * a string: The predicate name to follow into this node\n  * a list of strings: The predicates to follow into this node\n  * a query path object: The target of which is a set of predicates to follow.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the predicate used to the output set.\n  * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n\nExample:\n\n```javascript\n// Find the cool people, bob, dani and greg\ng.V(\"cool_person\")\n  .in(\"<status>\")\n  .all();\n// Find who follows bob, in this case, alice, charlie, and dani\ng.V(\"<bob>\")\n  .in(\"<follows>\")\n  .all();\n// Find who follows the people emily follows, namely, bob and emily\ng.V(\"<emily>\")\n  .out(\"<follows>\")\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.inPredicates()`\n\nInPredicates gets the list of predicates that are pointing in to a node.\n\nExample:\n\n```javascript\n// bob only has \"<follows>\" predicates pointing inward\n// returns \"<follows>\"\ng.V(\"<bob>\")\n  .inPredicates()\n  .all();\n```\n\n### `path.intersect(path)`\n\nIntersect filters all paths by the result of another query path.\n\nThis is essentially a join where, at the stage of each path, a node is shared. Example:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\ncFollows.intersect(dFollows).all();\n// Equivalently, g.V(\"<charlie>\").out(\"<follows>\").And(g.V(\"<dani>\").out(\"<follows>\")).all()\n```\n\n### `path.is(node, [node..])`\n\nFilter all paths to ones which, at this point, are on the given node.\n\nArguments:\n\n* `node`: A string for a node. Can be repeated or a list of strings.\n\nExample:\n\n```javascript\n// Starting from all nodes in the graph, find the paths that follow bob.\n// Results in three paths for bob (from alice, charlie and dani).all()\ng.V()\n  .out(\"<follows>\")\n  .is(\"<bob>\")\n  .all();\n```\n\n### `path.labelContext([labelPath], [tags])`\n\nLabelContext sets \\(or removes\\) the subgraph context to consider in the following traversals. Affects all In\\(\\), Out\\(\\), and Both\\(\\) calls that follow it. The default LabelContext is null \\(all subgraphs\\).\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: In future traversals, consider all edges, regardless of subgraph.\n  * a string: The name of the subgraph to restrict traversals to.\n  * a list of strings: A set of subgraphs to restrict traversals to.\n  * a query path object: The target of which is a set of subgraphs.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the last traversed label to the output set.\n  * a list of strings: Multiple tags to use as keys to save the label used to the output set.\n\nExample:\n\n```javascript\n// Find the status of people Dani follows\ng.V(\"<dani>\")\n  .out(\"<follows>\")\n  .out(\"<status>\")\n  .all();\n// Find only the statuses provided by the smart_graph\ng.V(\"<dani>\")\n  .out(\"<follows>\")\n  .labelContext(\"<smart_graph>\")\n  .out(\"<status>\")\n  .all();\n// Find all people followed by people with statuses in the smart_graph.\ng.V()\n  .labelContext(\"<smart_graph>\")\n  .in(\"<status>\")\n  .labelContext(null)\n  .in(\"<follows>\")\n  .all();\n```\n\n### `path.labels()`\n\nLabels gets the list of inbound and outbound quad labels\n\n### `path.limit(limit)`\n\nLimit limits a number of nodes for current path.\n\nArguments:\n\n* `limit`: A number of nodes to limit results to.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .limit(2)\n  .all();\n```\n\n### `path.map(*)`\n\nMap is a alias for ForEach.\n\n### `path.or(path)`\n\nOr is an alias for Union.\n\n### `path.out([predicatePath], [tags])`\n\nOut is the work-a-day way to get between nodes, in the forward direction. Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects.\n\nArguments:\n\n* `predicatePath` \\(Optional\\): One of:\n  * null or undefined: All predicates pointing out from this node\n  * a string: The predicate name to follow out from this node\n  * a list of strings: The predicates to follow out from this node\n  * a query path object: The target of which is a set of predicates to follow.\n* `tags` \\(Optional\\): One of:\n  * null or undefined: No tags\n  * a string: A single tag to add the predicate used to the output set.\n  * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n\nExample:\n\n```javascript\n// The working set of this is bob and dani\ng.V(\"<charlie>\")\n  .out(\"<follows>\")\n  .all();\n// The working set of this is fred, as alice follows bob and bob follows fred.\ng.V(\"<alice>\")\n  .out(\"<follows>\")\n  .out(\"<follows>\")\n  .all();\n// Finds all things dani points at. Result is bob, greg and cool_person\ng.V(\"<dani>\")\n  .out()\n  .all();\n// Finds all things dani points at on the status linkage.\n// Result is bob, greg and cool_person\ng.V(\"<dani>\")\n  .out([\"<follows>\", \"<status>\"])\n  .all();\n// Finds all things dani points at on the status linkage, given from a separate query path.\n// Result is {\"id\": \"cool_person\", \"pred\": \"<status>\"}\ng.V(\"<dani>\")\n  .out(g.V(\"<status>\"), \"pred\")\n  .all();\n```\n\n### `path.outPredicates()`\n\nOutPredicates gets the list of predicates that are pointing out from a node.\n\nExample:\n\n```javascript\n// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n// returns \"<follows>\", \"<status>\"\ng.V(\"<bob>\")\n  .outPredicates()\n  .all();\n```\n\n### `path.save(predicate, tag)`\n\nSave saves the object of all quads with predicate into tag, without traversal.\n\nArguments:\n\n* `predicate`: A string for a predicate node.\n* `tag`: A string for a tag key to store the object node.\n\nExample:\n\n```javascript\n// Start from dani and bob and save who they follow into \"target\"\n// Returns:\n//   {\"id\" : \"<bob>\", \"target\": \"<fred>\" },\n//   {\"id\" : \"<dani>\", \"target\": \"<bob>\" },\n//   {\"id\" : \"<dani>\", \"target\": \"<greg>\" }\ng.V(\"<dani>\", \"<bob>\")\n  .save(\"<follows>\", \"target\")\n  .all();\n```\n\n### `path.saveInPredicates(tag)`\n\nSaveInPredicates tags the list of predicates that are pointing in to a node.\n\nExample:\n\n```javascript\n// bob only has \"<follows>\" predicates pointing inward\n// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\ng.V(\"<bob>\")\n  .saveInPredicates(\"pred\")\n  .all();\n```\n\n### `path.saveOpt(*)`\n\nSaveOpt is the same as Save, but returns empty tags if predicate does not exists.\n\n### `path.saveOptR(*)`\n\nSaveOptR is the same as SaveOpt, but tags values via reverse predicate.\n\n### `path.saveOutPredicates(tag)`\n\nSaveOutPredicates tags the list of predicates that are pointing out from a node.\n\nExample:\n\n```javascript\n// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\ng.V(\"<bob>\")\n  .saveInPredicates(\"pred\")\n  .all();\n```\n\n### `path.saveR(*)`\n\nSaveR is the same as Save, but tags values via reverse predicate.\n\n### `path.skip(offset)`\n\nSkip skips a number of nodes for current path.\n\nArguments:\n\n* `offset`: A number of nodes to skip.\n\nExample:\n\n```javascript\n// Start from all nodes that follow bob, and skip 2 nodes -- results in dani\ng.V()\n  .has(\"<follows>\", \"<bob>\")\n  .skip(2)\n  .all();\n```\n\n### `path.tag(tags)`\n\nTag saves a list of nodes to a given tag.\n\nIn order to save your work or learn more about how a path got to the end, we have tags. The simplest thing to do is to add a tag anywhere you'd like to put each node in the result set.\n\nArguments:\n\n* `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached \"Tag\"\n\n  Example:\n\n```javascript\n// Start from all nodes, save them into start, follow any status links, and return the result.\n// Results are:\n//   {\"id\": \"cool_person\", \"start\": \"<bob>\"},\n//   {\"id\": \"cool_person\", \"start\": \"<dani>\"},\n//   {\"id\": \"cool_person\", \"start\": \"<greg>\"},\n//   {\"id\": \"smart_person\", \"start\": \"<emily>\"},\n//   {\"id\": \"smart_person\", \"start\": \"<greg>\"}\ng.V()\n  .tag(\"start\")\n  .out(\"<status>\")\n  .all();\n```\n\n### `path.tagArray(*)`\n\nTagArray is the same as ToArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment.\n\nExample:\n\n```javascript\n// bobTags contains an Array of followers of bob (alice, charlie, dani).\nvar bobTags = g\n  .V(\"<bob>\")\n  .tag(\"name\")\n  .in(\"<follows>\")\n  .tagArray();\n// nameValue should be the string \"<bob>\"\nvar nameValue = bobTags[0][\"name\"];\n```\n\n### `path.tagValue()`\n\nTagValue is the same as TagArray, but limited to one result node. Returns a tag-to-string map.\n\n### `path.toArray(*)`\n\nToArray executes a query and returns the results at the end of the query path as an JS array.\n\nExample:\n\n```javascript\n// bobFollowers contains an Array of followers of bob (alice, charlie, dani).\nvar bobFollowers = g\n  .V(\"<bob>\")\n  .in(\"<follows>\")\n  .toArray();\n```\n\n### `path.toValue()`\n\nToValue is the same as ToArray, but limited to one result node.\n\n### `path.union(path)`\n\nUnion returns the combined paths of the two queries.\n\nNotice that it's per-path, not per-node. Once again, if multiple paths reach the same destination, they might have had different ways of getting there \\(and different tags\\). See also: `path.Tag()`\n\nExample:\n\n```javascript\nvar cFollows = g.V(\"<charlie>\").out(\"<follows>\");\nvar dFollows = g.V(\"<dani>\").out(\"<follows>\");\n// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), dani, bob (from dani), and greg.\ncFollows.union(dFollows).all();\n```\n\n### `path.unique()`\n\nUnique removes duplicate values from the path.\n\n### `path.order()`\n\nOrder returns values from the path in ascending order.\n\n"
  },
  {
    "path": "docs/query-languages/graphql.md",
    "content": "# GraphQL Guide\n\n**Disclaimer:** Cayley's GraphQL implementation is not strictly a GraphQL, but only a query language with the same syntax and mostly the same rules.\n\nWe will use [this simple dataset](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/data/testdata.nq) for our examples.\n\nEvery query is represented by tree-like structure of nested objects and properties, similar to [MQL](mql.md).\n\n```graphql\n{\n  nodes{\n    id\n  }\n}\n```\n\nThis particular query is equivalent to all nodes in the graph, where `id` is the special field name for the value of the node itself.\n\nFirst root object in traditional GraphQL \\(named `nodes` here\\) represents a method that will be called on the server to get results. In our current implementation this name serves only as a placeholder and will always execute the object search query.\n\nOur example returns the following result:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\"},\n      {\"id\": \"status\"},\n      {\"id\": \"cool_person\"},\n      {\"id\": \"alice\"},\n      {\"id\": \"greg\"},\n      {\"id\": \"emily\"},\n      {\"id\": \"smart_graph\"},\n      {\"id\": \"predicates\"},\n      {\"id\": \"dani\"},\n      {\"id\": \"fred\"},\n      {\"id\": \"smart_person\"},\n      {\"id\": \"charlie\"},\n      {\"id\": \"are\"},\n      {\"id\": \"follows\"}\n    ]\n  }\n}\n```\n\nFirst level of JSON object corresponds to a request itself, thus either `data` field or `errors` will be present.\n\nAny nested objects will correspond to fields defined in query, including top-level name \\(`nodes`\\).\n\n## Limit and pagination\n\nMaximal number of results can be limited using `first` keyword:\n\n```graphql\n{\n  nodes(first: 10){\n    id\n  }\n}\n```\n\nPagination can be done with `offset` keyword:\n\n```graphql\n{\n  nodes(offset: 5, first: 3){\n    id\n  }\n}\n```\n\nThis query returns objects 5-7.\n\n_Note: Values might be sorted differently, depending on what backend is used._\n\n## Properties\n\nPredicates \\(or properties\\) are added to the object to specify additional fields to load:\n\n```graphql\n{\n  nodes{\n    id, status\n  }\n}\n```\n\nResults:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\", \"status\": \"cool_person\"},\n      {\"id\": \"greg\", \"status\": \"cool_person\"},\n      {\"id\": \"dani\", \"status\": \"cool_person\"},\n      {\"id\": \"greg\", \"status\": \"smart_person\"},\n      {\"id\": \"emily\", \"status\": \"smart_person\"}\n    ]\n  }\n}\n```\n\nAll predicates are interpreted as IRIs and can be written in plain text or with angle brackets: `status` and `<status>` are considered equal. Also, well-known namespaces like RDF, RDFS and Schema.org can be written in short form and will be expanded automatically: `schema:name` and `<schema:name>` will be expanded to `<http://schema.org/name>`.\n\nProperties are required to be present by default and can be set to optional with `@opt` or `@optional` directive:\n\n```graphql\n{\n  nodes{\n    id\n    status @opt\n  }\n}\n```\n\nResults:\n\n```javascript\n{\n  \"data\": {\n    \"nodes\": [\n      {\"id\": \"bob\", \"status\": \"cool_person\"},\n      {\"id\": \"status\"},\n      {\"id\": \"cool_person\"},\n      {\"id\": \"alice\"},\n      {\"id\": \"greg\", \"status\": [\"cool_person\", \"smart_person\"]},\n      {\"id\": \"emily\", \"status\": \"smart_person\"},\n      {\"id\": \"smart_graph\"},\n      {\"id\": \"predicates\"},\n      {\"id\": \"dani\", \"status\": \"cool_person\"},\n      {\"id\": \"fred\"},\n      {\"id\": \"smart_person\"},\n      {\"id\": \"charlie\"},\n      {\"id\": \"are\"},\n      {\"id\": \"follows\"}\n    ]\n  }\n}\n```\n\n_Note: Since Cayley has no knowledge about property types and schema, it might decide to return a property as a single value for one object and as an array for another object. This behavior will be fixed in future versions._\n\n## Nested objects\n\nObjects and properties can be nested:\n\n```graphql\n{\n  nodes{\n    id\n    follows {\n      id\n    }\n  }\n}\n```\n\nAll operations available on root also works for nested object, for example the limit:\n\n```graphql\n{\n  nodes(first: 10){\n    id\n    follows(first: 1){\n      id\n    }\n  }\n}\n```\n\n## Reversed predicates\n\nAny predicate can be reversed with `@rev` or `@reverse` directive \\(search for \"in\" links instead of \"out\"\\):\n\n```graphql\n{\n  nodes{\n    id\n    followed: <follows> @rev {\n      id\n    }\n  }\n}\n```\n\n## Filters\n\nObjects can be filtered by specific values of properties:\n\n```graphql\n{\n  nodes(id: <bob>, status: \"cool_person\"){\n    id\n  }\n}\n```\n\nOnly exact match is supported for now.\n\nGraphQL names are interpreted as IRIs and string literals are interpreted as strings. Boolean, integer and float value are also supported and will be converted to `schema:Boolean`, `schema:Integer` and `schema:Float` accordingly.\n\n## Labels\n\nAny fields and traversals can be filtered by quad label with `@label` directive:\n\n```graphql\n{\n  nodes{\n    id\n    follows @label(v: <fb>) {\n      id, name\n      follows @label {\n        id, name\n      }\n    }\n  }\n}\n```\n\nLabel will be inherited by child objects. To reset label filter add `@label` directive without parameters.\n\n## Expanding all properties\n\nTo expand all properties of an object, `*` can be used instead of property name:\n\n```graphql\n{\n  nodes{\n    id\n    follows {*}\n  }\n}\n```\n\n## Un-nest objects\n\nThe following query will return objects with `{id: x, status: {name: y}}` structure:\n\n```graphql\n{\n  nodes{\n    id\n    status {\n      name\n    }\n  }\n}\n```\n\nIt is possible to un-nest `status` field object into parent:\n\n```graphql\n{\n  nodes{\n    id\n    status @unnest {\n      status: name\n    }\n  }\n}\n```\n\nResulted objects will have a flat structure: `{id: x, status: y}`.\n\nArrays fields cannot be un-nested. You can still un-nest such fields by providing a limit directive \\(will select the first value from array\\):\n\n```graphql\n{\n  nodes{\n    id\n    statuses(first: 1) @unnest {\n      status: name\n    }\n  }\n}\n```\n\n"
  },
  {
    "path": "docs/query-languages/mql.md",
    "content": "# MQL Guide\n\n## General\n\nCayley's MQL implementation is a work-in-progress clone of [Freebase's MQL API](https://developers.google.com/freebase/mql/). At the moment, it supports very basic queries without some of the extended features. It also aims to be database-agnostic, meaning that the schema inference from Freebase does not \\(yet\\) apply.\n\nEvery JSON Object can be thought of as a node in the graph, and wrapping an object in a list means there may be several of these, or it may be repeated. A simple query like:\n\n```javascript\n[{\n  \"id\": null\n}]\n```\n\nIs equivalent to all nodes in the graph, where \"id\" is the special keyword for the value of the node.\n\nPredicates are added to the object to specify constraints.\n\n```javascript\n[{\n  \"id\": null,\n  \"some_predicate\": \"some value\"\n}]\n```\n\nPredicates can take as values objects or lists of objects \\(subqueries\\), strings and numbers \\(literal IDs that must match -- equivalent to the object {\"id\": \"value\"}\\) or null, which indicates that, while the object must have a predicate that matches, the matching values will replace the null. A single null is one such value, an empty list will be filled with all such values, as strings.\n\n## Keywords\n\n* `id`: The value of the node.\n\n## Reverse Predicates\n\nPredicates always assume a forward direction. That is,\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"some_predicate\": \"B\"\n}]\n```\n\nwill only match if the quad\n\n```text\nA some_predicate B .\n```\n\nexists. In order to reverse the directions, \"!predicates\" are used. So that:\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"!some_predicate\": \"B\"\n}]\n```\n\nwill only match if the quad\n\n```text\nB some_predicate A .\n```\n\nexists.\n\n## Multiple Predicates\n\nJSON does not specify the behavior of objects with the same key. In order to have separate constraints for the same predicate, the prefix \"@name:\" can be applied to any predicate. This is slightly different from traditional MQL in that fully-qualified http paths may be common predicates, so we have an \"@name:\" prefix instead.\n\n```javascript\n[{\n  \"id\": \"A\",\n  \"@x:some_predicate\": \"B\",\n  \"@y:some_predicate\": \"C\"\n\n}]\n```\n\nWill only match if _both_\n\n```text\nA some_predicate B .\nA some_predicate C .\n```\n\nexist.\n\nThis combines with the reversal rule to create paths like `\"@a:!some_predicate\"`\n\n"
  },
  {
    "path": "docs/quickstart-as-application.md",
    "content": "# Quickstart-As-Application\n\nSee [Getting Started](https://github.com/cayleygraph/cayley/blob/master/docs/getting-started.md) and [Installation](https://github.com/cayleygraph/cayley/blob/master/docs/installation.md)\n\n"
  },
  {
    "path": "docs/quickstart-as-lib.md",
    "content": "# Quickstart as Library\n\nCurrently, Cayley supports being used as a Go library for other projects. To use it in such a way, here's a quick example:\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n\n    \"github.com/cayleygraph/cayley\"\n    \"github.com/cayleygraph/quad\"\n)\n\nfunc main() {\n    // Create a brand new graph\n    store, err := cayley.NewMemoryGraph()\n    if err != nil {\n        log.Fatalln(err)\n    }\n\n    store.AddQuad(quad.Make(\"phrase of the day\", \"is of course\", \"Hello World!\", nil))\n\n    // Now we create the path, to get to our data\n    p := cayley.StartPath(store, quad.String(\"phrase of the day\")).Out(quad.String(\"is of course\"))\n\n    // Now we iterate over results. Arguments:\n    // 1. Optional context used for cancellation.\n    // 2. Flag to optimize query before execution.\n    // 3. Quad store, but we can omit it because we have already built path with it.\n    err = p.Iterate(nil).EachValue(nil, func(value quad.Value){\n        nativeValue := quad.NativeOf(value) // this converts RDF values to normal Go types\n        fmt.Println(nativeValue)\n    })\n    if err != nil {\n        log.Fatalln(err)\n    }\n}\n```\n\nTo use other backends, you can empty-import them, eg\n\n```go\nimport _ \"github.com/cayleygraph/cayley/graph/kv/bolt\"\n```\n\nAnd use them with a call like\n\n```go\nimport \"github.com/cayleygraph/cayley/graph\"\n\nfunc open() {\n  // Initialize the database\n  graph.InitQuadStore(\"bolt\", path, nil)\n\n  // Open and use the database\n  cayley.NewGraph(\"bolt\", path, nil)\n}\n```\n\nMore runnable examples are available in [examples](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/examples/README.md) folder.\n\n"
  },
  {
    "path": "docs/todo.md",
    "content": "# TODOs\n\nThe main source of our TODO list is our [Github Issues](https://github.com/cayleygraph/cayley/issues), so we are going to try to avoid duplicating them here.\n\n## Anything marked \"TODO\" in the code.\n\nUsually something that should be taken care of.\n\n"
  },
  {
    "path": "docs/tools/convert-linked-data-files.md",
    "content": "---\ndescription: >-\n  Linked Data has multiple representations. The Cayley CLI includes a utility to\n  convert Linked Data files from one format to another.\n---\n\n# Convert Linked Data files\n\n## Convert from one format to another\n\n```\n$ cayley convert -i data.jsonld -o data.nquads\n```\n\n`-i` is the input file to be converted. In this example it is a [JSON-LD](https://www.w3.org/TR/json-ld11/) file named `data.jsonld`.\n\n`-o` is the file to be created in the desired format. In this example it is a [N-Quads](https://www.w3.org/TR/n-quads/) file named `data.nquads`.\n\n### Explicitly specify formats\n\nThe formats of the input and output files are detected automatically by the file extension. In case a specific format should be used for input or output use `--load_format` and `--dump_format` respectively.\n\n```text\n$ cayley convet -i data.jsonld -o data --dump_format pquads\n```\n\n`--dump_format` is set to the P-Quads format, a binary format used internally in Cayley.\n\n"
  },
  {
    "path": "docs/ui-overview.md",
    "content": "# UI Overview\n\n## Sidebar\n\nAlong the side are the various actions or views you can take. From the top, these are:\n\n* Run Query \\(run the query\\)\n* Gizmo \\(a dropdown, to pick your query language, MQL is the other\\)\n  * [GizmoAPI.md](gizmoapi.md): This is the one of the two query languages used either via the REPL or HTTP interface.\n  * [MQL.md](mql.md): The _other_ query language the interfaces support.\n* Query \\(a request/response editor for the query language\\)\n* Query Shape \\(a visualization of the shape of the final query. Does not execute the query.\\)\n* Visualize \\(runs a query and, if tagged correctly, gives a sigmajs view of the results\\)\n* Write \\(an interface to write or remove individual quads or quad files\\)\n* Documentation \\(this documentation\\)\n\n## Visualize\n\nTo use the visualize function, emit, either through tags or JS post-processing, a set of JSON objects containing the keys `source` and `target`. These will be the links, and nodes will automatically be detected.\n\nFor example:\n\n```javascript\n[\n  {\n    source: \"node1\",\n    target: \"node2\"\n  },\n  {\n    source: \"node1\",\n    target: \"node3\"\n  }\n];\n```\n\nOther keys are ignored. The upshot is that if you use the \"Tag\" functionality to add \"source\" and \"target\" tags, you can extract and quickly view subgraphs.\n\n```text\n// Visualize who dani follows.\ng.V(\"<dani>\").Tag(\"source\").Out(\"<follows>\").Tag(\"target\").All()\n```\n\nThe visualizer expects to tag nodes as either \"source\" or \"target.\" Your source is represented as a blue node. While your target is represented as an orange node. The idea being that our node relationship goes from blue to orange \\(source to target\\).\n\n"
  },
  {
    "path": "docs/usage/3rd-party-apis.md",
    "content": "# 3rd-Party-APIs\n\nVarious 3rd party APIs\n\n* **Clojure**: [https://github.com/wgb/cayley-clj](https://github.com/wgb/cayley-clj)\n* **Javascript/NodeJS**: [https://github.com/lnshi/node-cayley](https://github.com/lnshi/node-cayley), [https://github.com/villadora/cayley.js](https://github.com/villadora/cayley.js)\n* **Ruby**: [https://github.com/reneklacan/cayley-ruby](https://github.com/reneklacan/cayley-ruby)\n* **PHP**: [https://github.com/mcuadros/php-cayley](https://github.com/mcuadros/php-cayley)\n* **Python**: [https://github.com/ziyasal/pyley](https://github.com/ziyasal/pyley)\n* **.NET**: [https://github.com/ziyasal/Cayley.Net](https://github.com/ziyasal/Cayley.Net)\n* **Rust** \\(in early stage\\): [https://github.com/shamansir/cayley-rust](https://github.com/shamansir/cayley-rust)\n* **Haskell**: [https://github.com/MichelBoucey/cayley-client](https://github.com/MichelBoucey/cayley-client)\n\n"
  },
  {
    "path": "docs/usage/advanced-use.md",
    "content": "# Advanced Use\n\n## Initialize A Graph\n\nNow that Cayley is downloaded \\(or built\\), let's create our database. `init` is the subcommand to set up a database and the right indices.\n\nYou can set up a full [configuration file](../configuration.md) if you'd prefer, but it will also work from the command line.\n\nExamples for each backend can be found in `store.address` format from [config file](../configuration.md).\n\nThose two options \\(db and dbpath\\) are always going to be present. If you feel like not repeating yourself, setting up a configuration file for your backend might be something to do now. There's an example file, `cayley_example.yml` in the root directory.\n\nYou can repeat the `--db (-i)` and `--dbpath (-a)` flags from here forward instead of the config flag, but let's assume you created `cayley_overview.yml`\n\nNote: when you specify parameters in the config file the config flags \\(command line arguments\\) are ignored.\n\n## Load Data Into A Graph\n\nAfter the database is initialized we load the data.\n\n```bash\n./cayley load -c cayley_overview.yml -i data/testdata.nq\n```\n\nAnd wait. It will load. If you'd like to watch it load, you can run\n\n```bash\n./cayley load -c cayley_overview.yml -i data/testdata.nq --alsologtostderr=true\n```\n\nAnd watch the log output go by.\n\nIf you plan to import a large dataset into Cayley and try multiple backends, it makes sense to first convert the dataset to Cayley-specific binary format by running:\n\n```bash\n./cayley conv -i dataset.nq.gz -o dataset.pq.gz\n```\n\nThis will minimize parsing overhead on future imports and will compress dataset a bit better.\n\n## Connect a REPL To Your Graph\n\nNow it's loaded. We can use Cayley now to connect to the graph. As you might have guessed, that command is:\n\n```bash\n./cayley repl -c cayley_overview.yml\n```\n\nWhere you'll be given a `cayley>` prompt. It's expecting Gizmo/JS, but that can also be configured with a flag.\n\nNew nodes and links can be added with the following command:\n\n```bash\ncayley> :a subject predicate object label .\n```\n\nRemoving links works similarly:\n\n```bash\ncayley> :d subject predicate object .\n```\n\nThis is great for testing, and ultimately also for scripting, but the real workhorse is the next step.\n\nGo ahead and give it a try:\n\n```text\n// Simple math\ncayley> 2 + 2\n\n// JavaScript syntax\ncayley> x = 2 * 8\ncayley> x\n\n// See all the entities in this small follow graph.\ncayley> graph.Vertex().All()\n\n// See only dani.\ncayley> graph.Vertex(\"<dani>\").All()\n\n// See who dani follows.\ncayley> graph.Vertex(\"<dani>\").Out(\"<follows>\").All()\n```\n\n## Serve Your Graph\n\nJust as before:\n\n```bash\n./cayley http -c cayley_overview.yml\n```\n\nAnd you'll see a message not unlike\n\n```bash\nlistening on :64210, web interface at http://localhost:64210\n```\n\nIf you visit that address \\(often, [http://localhost:64210](http://localhost:64210)\\) you'll see the full web interface and also have a graph ready to serve queries via the [HTTP API](http.md)\n\n### Access from other machines\n\nWhen you want to reach the API or UI from another machine in the network you need to specify the host argument:\n\n```bash\n./cayley http --config=cayley.cfg.overview --host=0.0.0.0:64210\n```\n\nThis makes it listen on all interfaces. You can also give it the specific the IP address you want Cayley to bind to.\n\n**Warning**: for security reasons you might not want to do this on a public accessible machine.\n\n"
  },
  {
    "path": "docs/usage/http.md",
    "content": "# HTTP Methods\n\nThis file covers deprecated v1 HTTP API. All the methods of v2 HTTP API is described in OpenAPI/Swagger [spec](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/docs/api/swagger.yml) and can be viewed by importing `https://raw.githubusercontent.com/cayleygraph/cayley/master/docs/api/swagger.yml` URL into [Swagger Editor](https://editor.swagger.io/) or [Swagger UI demo](http://petstore.swagger.io/).\n\n## Gephi\n\nCayley supports streaming to Gephi via [GraphStream](../query-languages/gephigraphstream.md).\n\n## API v1\n\nUnless otherwise noted, all URIs take a POST command.\n\n### Queries and Results\n\n#### `/api/v1/query/gizmo`\n\nPOST Body: Javascript source code of the query\n\nResponse: JSON results, depending on the query.\n\n#### `/api/v1/query/graphql`\n\nPOST Body: [GraphQL](../query-languages/graphql.md) query\n\nResponse: JSON results, depending on the query.\n\n#### `/api/v1/query/mql`\n\nPOST Body: JSON MQL query\n\nResponse: JSON results, with a query wrapper:\n\n```javascript\n{\n    \"result\": <JSON Result set>\n}\n```\n\nIf the JSON is invalid or an error occurs:\n\n```javascript\n{\n    \"error\": \"Error message\"\n}\n```\n\n### Query Shapes\n\nResult form:\n\n```javascript\n{\n    \"nodes\": [{\n        \"id\" : integer,\n        \"tags\": [\"list of tags from the query\"],\n        \"values\": [\"known values from the query\"],\n        \"is_link_node\": bool,  // Does the node represent the link or the node (the oval shapes)\n        \"is_fixed\": bool  // Is the node a fixed starting point of the query\n    }],\n\n    \"links\": [{\n        \"source\": integer,  // Node ID\n        \"target\": integer,  // Node ID\n        \"link_node\": integer  // Node ID\n    }]\n}\n```\n\n#### `/api/v1/shape/gizmo`\n\nPOST Body: Javascript source code of the query\n\nResponse: JSON description of the last query executed.\n\n#### `/api/v1/shape/mql`\n\nPOST Body: JSON MQL query\n\nResponse: JSON description of the query.\n\n### Write commands\n\nResponses come in the form\n\n200 Success:\n\n```javascript\n{\n    \"result\": \"Success message.\"\n}\n```\n\n400 / 500 Error:\n\n```javascript\n{\n    \"error\": \"Error message.\"\n}\n```\n\n#### `/api/v1/write`\n\nPOST Body: JSON quads\n\n```javascript\n[{\n    \"subject\": \"Subject Node\",\n    \"predicate\": \"Predicate Node\",\n    \"object\": \"Object node\",\n    \"label\": \"Label node\"  // Optional\n}]   // More than one quad allowed.\n```\n\nResponse: JSON response message\n\n#### `/api/v1/write/file/nquad`\n\nPOST Body: Form-encoded body:\n\n* Key: `NQuadFile`, Value: N-Quad file to write.\n\nResponse: JSON response message\n\nExample:\n\n```text\ncurl http://localhost:64210/api/v1/write/file/nquad -F NQuadFile=@30k.n3\n```\n\n#### `/api/v1/delete`\n\nPOST Body: JSON quads\n\n```javascript\n[{\n    \"subject\": \"Subject Node\",\n    \"predicate\": \"Predicate Node\",\n    \"object\": \"Object node\",\n    \"label\": \"Label node\"  // Optional\n}]   // More than one quad allowed.\n```\n\nResponse: JSON response message.\n\n"
  },
  {
    "path": "docs/usage/migration.md",
    "content": "# Migration\n\n## From Cayley 0.6.1 to 0.7.x\n\nFirst you need to dump all the data from the database via v0.6.1:\n\n```bash\n./cayley-0.6 dump --db <backend> --dbpath <address> --dump_type pquads --dump ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley-0.6 dump --config <config> --dump_type pquads --dump ./data.pq.gz\n```\n\nAnd load the data into new database via v0.7.x \\(`pq` file extension is important\\):\n\n```bash\n./cayley load --init -d <new-backend> -a <new-address> -i ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley load --init -c <new-config> -i ./data.pq.gz\n```\n\n### Dump via text format\n\nAn above guide uses Cayley-specific binary format to avoid encoding and parsing overhead and to compress output file better.\n\nAs an alternative, a standard nquads file format can be used to dump and load data \\(note `nq` extension\\):\n\n```bash\n./cayley-0.6 dump --config <config> --dump ./data.nq.gz\n./cayley load --init -c <new-config> -i ./data.nq.gz\n```\n\n### Bolt, LevelDB, MongoDB\n\nCayley v0.7.0 is still able to read and write databases of these types in old format. It can be accessed by changing backend name from `bolt`/`leveldb`/`mongo` to `bolt1`/`leveldb1`/`mongo1`. Thus, you can use guide for moving data between different backends for v0.7.x \\(see below\\).\n\n**Note:** support for old versions will be dropped starting from v0.7.1.\n\n### SQL\n\nData format for SQL between v0.6.1 and v0.7.x has not changed significantly. Database can be upgraded by executing the following SQL statements:\n\n```sql\nALTER TABLE quads DROP id;\nALTER TABLE nodes ADD refs INT DEFAULT 0 NOT NULL;\nALTER TABLE nodes ALTER COLUMN refs DROP DEFAULT;\nUPDATE nodes SET refs = \n  (SELECT count(1) FROM quads WHERE subject_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE predicate_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE object_hash = nodes.hash) +\n  (SELECT count(1) FROM quads WHERE label_hash = nodes.hash);\n```\n\n## From different backend \\(Cayley 0.7+\\)\n\nFirst you need to dump all the data from old backend \\(`pq` extension is important\\):\n\n```bash\n./cayley dump -d <backend> -a <address> -o ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley dump -c <config> -o ./data.pq.gz\n```\n\nAnd load the data into a new backend and/or database:\n\n```bash\n./cayley load --init -d <new-backend> -a <new-address> -i ./data.pq.gz\n```\n\nor using config file:\n\n```bash\n./cayley load --init -c <new-config> -i ./data.pq.gz\n```\n\n### Dump via text format\n\nAn above guide uses Cayley-specific binary format to avoid encoding and parsing overhead and to compress output file better.\n\nAs an alternative, a standard nquads file format can be used to dump and load data \\(note `nq` extension\\):\n\n```bash\n./cayley dump -c <config> -o ./data.nq.gz\n./cayley load --init -c <new-config> -i ./data.nq.gz\n```\n\n"
  },
  {
    "path": "docs/usage/quickstart-as-lib.md",
    "content": "# Quickstart as Library\n\nCurrently, Cayley supports being used as a Go library for other projects. To use it in such a way, here's a quick example:\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n\n    \"github.com/cayleygraph/cayley\"\n    \"github.com/cayleygraph/quad\"\n)\n\nfunc main() {\n    // Create a brand new graph\n    store, err := cayley.NewMemoryGraph()\n    if err != nil {\n        log.Fatalln(err)\n    }\n\n    store.AddQuad(quad.Make(\"phrase of the day\", \"is of course\", \"Hello World!\", nil))\n\n    // Now we create the path, to get to our data\n    p := cayley.StartPath(store, quad.String(\"phrase of the day\")).Out(quad.String(\"is of course\"))\n\n    // Now we iterate over results. Arguments:\n    // 1. Optional context used for cancellation.\n    // 2. Flag to optimize query before execution.\n    // 3. Quad store, but we can omit it because we have already built path with it.\n    err = p.Iterate(nil).EachValue(nil, func(value quad.Value){\n        nativeValue := quad.NativeOf(value) // this converts RDF values to normal Go types\n        fmt.Println(nativeValue)\n    })\n    if err != nil {\n        log.Fatalln(err)\n    }\n}\n```\n\nTo use other backends, you can empty-import them, eg\n\n```go\nimport _ \"github.com/cayleygraph/cayley/graph/kv/bolt\"\n```\n\nAnd use them with a call like\n\n```go\nimport \"github.com/cayleygraph/cayley/graph\"\n\nfunc open() {\n  // Initialize the database\n  graph.InitQuadStore(\"bolt\", path, nil)\n\n  // Open and use the database\n  cayley.NewGraph(\"bolt\", path, nil)\n}\n```\n\nMore runnable examples are available in [examples](https://github.com/cayleygraph/cayley/tree/87c9c341848b59924a054ebc2dd0f2bf8c57c6a9/examples/README.md) folder.\n\n"
  },
  {
    "path": "docs/usage/ui-overview.md",
    "content": "# UI Overview\n\n## Sidebar\n\nAlong the side are the various actions or views you can take. From the top, these are:\n\n* Run Query \\(run the query\\)\n* Gizmo \\(a dropdown, to pick your query language, MQL is the other\\)\n  * [GizmoAPI.md](../query-languages/gizmoapi.md): This is the one of the two query languages used either via the REPL or HTTP interface.\n  * [MQL.md](../query-languages/mql.md): The _other_ query language the interfaces support.\n* Query \\(a request/response editor for the query language\\)\n* Query Shape \\(a visualization of the shape of the final query. Does not execute the query.\\)\n* Visualize \\(runs a query and, if tagged correctly, gives a sigmajs view of the results\\)\n* Write \\(an interface to write or remove individual quads or quad files\\)\n* Documentation \\(this documentation\\)\n\n## Visualize\n\nTo use the visualize function, emit, either through tags or JS post-processing, a set of JSON objects containing the keys `source` and `target`. These will be the links, and nodes will automatically be detected.\n\nFor example:\n\n```javascript\n[\n  {\n    source: \"node1\",\n    target: \"node2\"\n  },\n  {\n    source: \"node1\",\n    target: \"node3\"\n  }\n];\n```\n\nOther keys are ignored. The upshot is that if you use the \"Tag\" functionality to add \"source\" and \"target\" tags, you can extract and quickly view subgraphs.\n\n```text\n// Visualize who dani follows.\ng.V(\"<dani>\").Tag(\"source\").Out(\"<follows>\").Tag(\"target\").All()\n```\n\nThe visualizer expects to tag nodes as either \"source\" or \"target.\" Your source is represented as a blue node. While your target is represented as an orange node. The idea being that our node relationship goes from blue to orange \\(source to target\\).\n\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\nEach of the examples can be run with \n\n```go run hello_world/main.go```\n\nobviously changing **hello_world** to the one you want to run!\n\nThe hello_bolt example requires `go get`.\n"
  },
  {
    "path": "examples/hello_bolt/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/cayleygraph/cayley\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t_ \"github.com/cayleygraph/cayley/graph/kv/bolt\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc main() {\n\t// File for your new BoltDB. Use path to regular file and not temporary in the real world\n\ttmpdir, err := ioutil.TempDir(\"\", \"example\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdefer os.RemoveAll(tmpdir) // clean up\n\n\t// Initialize the database\n\terr = graph.InitQuadStore(\"bolt\", tmpdir, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Open and use the database\n\tstore, err := cayley.NewGraph(\"bolt\", tmpdir, nil)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tstore.AddQuad(quad.Make(\"phrase of the day\", \"is of course\", \"Hello BoltDB!\", \"demo graph\"))\n\n\t// Now we create the path, to get to our data\n\tp := cayley.StartPath(store, quad.String(\"phrase of the day\")).Out(quad.String(\"is of course\"))\n\n\t// This is more advanced example of the query.\n\t// Simpler equivalent can be found in hello_world example.\n\n\tctx := context.TODO()\n\t// Now we get an iterator for the path and optimize it.\n\t// The second return is if it was optimized, but we don't care for now.\n\tits, _ := p.BuildIterator(ctx).Optimize(ctx)\n\tit := its.Iterate()\n\n\t// remember to cleanup after yourself\n\tdefer it.Close()\n\n\t// While we have items\n\tfor it.Next(ctx) {\n\t\ttoken := it.Result()              // get a ref to a node (backend-specific)\n\t\tvalue, err := store.NameOf(token) // get the value in the node (RDF)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(err)\n\t\t}\n\t\tnativeValue := quad.NativeOf(value) // convert value to normal Go type\n\n\t\tfmt.Println(nativeValue) // print it!\n\t}\n\tif err := it.Err(); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n"
  },
  {
    "path": "examples/hello_schema/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"math/rand\"\n\t\"os\"\n\n\t\"github.com/cayleygraph/cayley\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t_ \"github.com/cayleygraph/cayley/graph/kv/bolt\"\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n\n\t// Import RDF vocabulary definitions to be able to expand IRIs like rdf:label.\n\t_ \"github.com/cayleygraph/quad/voc/core\"\n)\n\ntype Person struct {\n\t// dummy field to enforce all object to have a <id> <rdf:type> <ex:Person> relation\n\t// means nothing for Go itself\n\trdfType struct{} `quad:\"@type > ex:Person\"`\n\tID      quad.IRI `json:\"@id\"`     // tag @id is a special one - graph node value will be stored in this field\n\tName    string   `json:\"ex:name\"` // field name (predicate) may be written as json field name\n\tAge     int      `quad:\"ex:age\"`  // or in a quad tag\n}\n\ntype Coords struct {\n\t// Object may be without id - it will be generated automatically.\n\t// It's also not necessary to have a type definition.\n\tLat float64 `json:\"ex:lat\"`\n\tLng float64 `json:\"ex:lng\"`\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc main() {\n\t// Define an \"ex:\" prefix for IRIs that will be expanded to \"http://example.org\".\n\t// \"ex:name\" will become \"http://example.org/name\"\n\tvoc.RegisterPrefix(\"ex:\", \"http://example.org/\")\n\n\t// Associate Go type with an IRI.\n\t// All Coords objects will now generate a <id> <rdf:type> <ex:Coords> triple.\n\tschema.RegisterType(quad.IRI(\"ex:Coords\"), Coords{})\n\n\tsch := schema.NewConfig()\n\t// Override a function to generate IDs. Can be changed to generate UUIDs, for example.\n\tsch.GenerateID = func(_ interface{}) quad.Value {\n\t\treturn quad.BNode(fmt.Sprintf(\"node%d\", rand.Intn(1000)))\n\t}\n\n\t// File for your new BoltDB. Use path to regular file and not temporary in the real world\n\ttmpdir, err := ioutil.TempDir(\"\", \"example\")\n\tcheckErr(err)\n\n\tdefer os.RemoveAll(tmpdir) // clean up\n\n\t// Initialize the database\n\terr = graph.InitQuadStore(\"bolt\", tmpdir, nil)\n\tcheckErr(err)\n\n\t// Open and use the database\n\tstore, err := cayley.NewGraph(\"bolt\", tmpdir, nil)\n\tcheckErr(err)\n\tdefer store.Close()\n\tqw := graph.NewWriter(store)\n\n\t// Save an object\n\tbob := Person{\n\t\tID:   quad.IRI(\"ex:bob\").Full().Short(),\n\t\tName: \"Bob\", Age: 32,\n\t}\n\tfmt.Printf(\"saving: %+v\\n\", bob)\n\tid, err := sch.WriteAsQuads(qw, bob)\n\tcheckErr(err)\n\terr = qw.Close()\n\tcheckErr(err)\n\n\tfmt.Println(\"id for object:\", id, \"=\", bob.ID) // should be equal\n\n\t// Get object by id\n\tvar someone Person\n\terr = sch.LoadTo(nil, store, &someone, id)\n\tcheckErr(err)\n\tfmt.Printf(\"loaded: %+v\\n\", someone)\n\n\t// Or get all objects of type Person\n\tvar people []Person\n\terr = sch.LoadTo(nil, store, &people)\n\tcheckErr(err)\n\tfmt.Printf(\"people: %+v\\n\", people)\n\n\tfmt.Println()\n\n\t// Store objects with no ID and type\n\tcoords := []Coords{\n\t\t{Lat: 12.3, Lng: 34.5},\n\t\t{Lat: 39.7, Lng: 8.41},\n\t}\n\tqw = graph.NewWriter(store)\n\tfor _, c := range coords {\n\t\tid, err = sch.WriteAsQuads(qw, c)\n\t\tcheckErr(err)\n\t\tfmt.Println(\"generated id:\", id)\n\t}\n\terr = qw.Close()\n\tcheckErr(err)\n\n\t// Get coords back\n\tvar newCoords []Coords\n\terr = sch.LoadTo(nil, store, &newCoords)\n\tcheckErr(err)\n\tfmt.Printf(\"coords: %+v\\n\", newCoords)\n\n\t// Print quads\n\tfmt.Println(\"\\nquads:\")\n\tctx := context.TODO()\n\tit := store.QuadsAllIterator().Iterate()\n\tdefer it.Close()\n\tfor it.Next(ctx) {\n\t\tfmt.Println(store.Quad(it.Result()))\n\t}\n}\n"
  },
  {
    "path": "examples/hello_world/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/cayleygraph/cayley\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc main() {\n\t// Create a brand new graph\n\tstore, err := cayley.NewMemoryGraph()\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tstore.AddQuad(quad.Make(\"phrase of the day\", \"is of course\", \"Hello World!\", nil))\n\n\t// Now we create the path, to get to our data\n\tp := cayley.StartPath(store, quad.String(\"phrase of the day\")).Out(quad.String(\"is of course\"))\n\n\t// Now we iterate over results. Arguments:\n\t// 1. Optional context used for cancellation.\n\t// 2. Quad store, but we can omit it because we have already built path with it.\n\terr = p.Iterate(nil).EachValue(nil, func(value quad.Value) error {\n\t\tnativeValue := quad.NativeOf(value) // this converts RDF values to normal Go types\n\t\tfmt.Println(nativeValue)\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n"
  },
  {
    "path": "examples/transaction/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/cayleygraph/cayley\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc main() {\n\t// To see how most of this works, see hello_world -- this just add in a transaction\n\tstore, err := cayley.NewMemoryGraph()\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// Create a transaction of work to do\n\t// NOTE: the transaction is independent of the storage type, so comes from cayley rather than store\n\tt := cayley.NewTransaction()\n\tt.AddQuad(quad.Make(\"food\", \"is\", \"good\", nil))\n\tt.AddQuad(quad.Make(\"phrase of the day\", \"is of course\", \"Hello World!\", nil))\n\tt.AddQuad(quad.Make(\"cats\", \"are\", \"awesome\", nil))\n\tt.AddQuad(quad.Make(\"cats\", \"are\", \"scary\", nil))\n\tt.AddQuad(quad.Make(\"cats\", \"want to\", \"kill you\", nil))\n\n\t// Apply the transaction\n\terr = store.ApplyTransaction(t)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tp := cayley.StartPath(store, quad.String(\"cats\")).Out(quad.String(\"are\"))\n\n\terr = p.Iterate(nil).EachValue(nil, func(v quad.Value) error {\n\t\tfmt.Println(\"cats are\", v.Native())\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/cayleygraph/cayley\n\ngo 1.22\n\ntoolchain go1.22.5\n\nrequire (\n\tgithub.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca\n\tgithub.com/cayleygraph/quad v1.3.0\n\tgithub.com/cznic/mathutil v0.0.0-20181122101859-297441e03548\n\tgithub.com/dennwc/graphql v0.0.0-20180603144102-12cfed44bc5d\n\tgithub.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2\n\tgithub.com/fsouza/go-dockerclient v1.11.0\n\tgithub.com/go-sql-driver/mysql v1.8.1\n\tgithub.com/golang/glog v1.2.1\n\tgithub.com/hidal-go/hidalgo v0.3.0\n\tgithub.com/jackc/pgx/v5 v5.6.0\n\tgithub.com/julienschmidt/httprouter v1.3.0\n\tgithub.com/lib/pq v1.10.9\n\tgithub.com/mattn/go-sqlite3 v1.14.22\n\tgithub.com/peterh/liner v1.2.2\n\tgithub.com/piprate/json-gold v0.5.0\n\tgithub.com/prometheus/client_golang v1.19.1\n\tgithub.com/spf13/cobra v1.8.1\n\tgithub.com/spf13/viper v1.19.0\n\tgithub.com/stretchr/testify v1.9.0\n\tgithub.com/syndtr/goleveldb v1.0.0\n\tgithub.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43\n\tgolang.org/x/net v0.27.0\n\tgoogle.golang.org/appengine v1.6.8\n\tgoogle.golang.org/protobuf v1.34.2\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect\n\tgithub.com/DataDog/zstd v1.5.0 // indirect\n\tgithub.com/Masterminds/semver/v3 v3.2.1 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect\n\tgithub.com/OneOfOne/xxhash v1.2.8 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/boltdb/bolt v1.3.1 // indirect\n\tgithub.com/cenkalti/backoff v2.2.1+incompatible // indirect\n\tgithub.com/cespare/xxhash v1.1.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cockroachdb/errors v1.9.0 // indirect\n\tgithub.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect\n\tgithub.com/cockroachdb/pebble v0.0.0-20220318150003-0ad186894f6d // indirect\n\tgithub.com/cockroachdb/redact v1.1.3 // indirect\n\tgithub.com/containerd/containerd v1.7.19 // indirect\n\tgithub.com/containerd/continuity v0.4.3 // indirect\n\tgithub.com/containerd/log v0.1.0 // indirect\n\tgithub.com/d4l3k/messagediff v1.2.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/dgraph-io/badger/v2 v2.2007.4 // indirect\n\tgithub.com/dgraph-io/ristretto v0.1.0 // indirect\n\tgithub.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect\n\tgithub.com/dlclark/regexp2 v1.10.0 // indirect\n\tgithub.com/docker/docker v27.0.3+incompatible // indirect\n\tgithub.com/docker/go-connections v0.4.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dustin/go-humanize v1.0.0 // indirect\n\tgithub.com/fsnotify/fsnotify v1.7.0 // indirect\n\tgithub.com/getsentry/sentry-go v0.13.0 // indirect\n\tgithub.com/go-kivik/couchdb v2.0.0+incompatible // indirect\n\tgithub.com/go-kivik/kivik v2.0.0+incompatible // indirect\n\tgithub.com/go-kivik/pouchdb v2.0.1+incompatible // indirect\n\tgithub.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect\n\tgithub.com/go-stack/stack v1.8.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gopherjs/gopherjs v0.0.0-20220221023154-0b2280d3ff96 // indirect\n\tgithub.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect\n\tgithub.com/jackc/puddle/v2 v2.2.1 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/klauspost/compress v1.17.9 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/magiconair/properties v1.8.7 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.15 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/patternmatcher v0.6.0 // indirect\n\tgithub.com/moby/sys/sequential v0.5.0 // indirect\n\tgithub.com/moby/sys/user v0.1.0 // indirect\n\tgithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect\n\tgithub.com/morikuni/aec v1.0.0 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.0 // indirect\n\tgithub.com/opencontainers/runc v1.1.13 // indirect\n\tgithub.com/ory/dockertest v3.3.5+incompatible // indirect\n\tgithub.com/otiai10/copy v1.12.0 // indirect\n\tgithub.com/pborman/uuid v1.2.1 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.2 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/pquerna/cachecontrol v0.2.0 // indirect\n\tgithub.com/prometheus/client_model v0.5.0 // indirect\n\tgithub.com/prometheus/common v0.48.0 // indirect\n\tgithub.com/prometheus/procfs v0.12.0 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/rivo/uniseg v0.4.4 // indirect\n\tgithub.com/rogpeppe/go-internal v1.12.0 // indirect\n\tgithub.com/sagikazarmark/locafero v0.4.0 // indirect\n\tgithub.com/sagikazarmark/slog-shim v0.1.0 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/sourcegraph/conc v0.3.0 // indirect\n\tgithub.com/spf13/afero v1.11.0 // indirect\n\tgithub.com/spf13/cast v1.6.0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.1.1 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.3 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect\n\tgo.etcd.io/bbolt v1.3.10 // indirect\n\tgo.mongodb.org/mongo-driver v1.8.4 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.9.0 // indirect\n\tgolang.org/x/crypto v0.25.0 // indirect\n\tgolang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect\n\tgolang.org/x/sync v0.7.0 // indirect\n\tgolang.org/x/sys v0.22.0 // indirect\n\tgolang.org/x/text v0.16.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/olivere/elastic.v5 v5.0.86 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tgotest.tools/v3 v3.5.0 // indirect\n)\n\nreplace github.com/Sirupsen/logrus => github.com/Sirupsen/logrus v1.0.1\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\nfilippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=\ngithub.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=\ngithub.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=\ngithub.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=\ngithub.com/DataDog/zstd v1.5.0 h1:+K/VEwIAaPcHiMtQvpLD4lqW7f0Gk3xdYZmI1hD+CXo=\ngithub.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=\ngithub.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=\ngithub.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=\ngithub.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=\ngithub.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/aws/aws-sdk-go v1.29.11/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=\ngithub.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=\ngithub.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca h1:77KAMse6RWRpPfVnIZcAtJ/5ZK/oRCeY94ZjIWSbe0g=\ngithub.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca/go.mod h1:TWe0N2hv5qvpLHT+K16gYcGBllld4h65dQ/5CNuirmk=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=\ngithub.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=\ngithub.com/cayleygraph/quad v1.3.0 h1:xg7HOLWWPgvZ4CcvzEpfCwq42L8mzYUR+8V0jtYoBzc=\ngithub.com/cayleygraph/quad v1.3.0/go.mod h1:NadtM7uMm78FskmX++XiOOrNvgkq0E1KvvhQdMseMz4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=\ngithub.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=\ngithub.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI=\ngithub.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=\ngithub.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=\ngithub.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0=\ngithub.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88=\ngithub.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ=\ngithub.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=\ngithub.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=\ngithub.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=\ngithub.com/cockroachdb/pebble v0.0.0-20220318150003-0ad186894f6d h1:VHoyzrRXf6pO4+3qyR7T0ez9rYT5GLTDcKk0epdumt4=\ngithub.com/cockroachdb/pebble v0.0.0-20220318150003-0ad186894f6d/go.mod h1:buxOO9GBtOcq1DiXDpIPYrmxY020K2A8lOrwno5FetU=\ngithub.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=\ngithub.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=\ngithub.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=\ngithub.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=\ngithub.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=\ngithub.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=\ngithub.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE=\ngithub.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc=\ngithub.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=\ngithub.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=\ngithub.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=\ngithub.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=\ngithub.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=\ngithub.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=\ngithub.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dennwc/graphql v0.0.0-20180603144102-12cfed44bc5d h1:QWlaiMNg63HE5qimJd4stjg9l1Ca4BKcgs+UNSWPJ+s=\ngithub.com/dennwc/graphql v0.0.0-20180603144102-12cfed44bc5d/go.mod h1:lg9KQn0BgRCSCGNpcGvJp/0Ljf1Yxk8TZq9HSYc43fk=\ngithub.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=\ngithub.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=\ngithub.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=\ngithub.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=\ngithub.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=\ngithub.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=\ngithub.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=\ngithub.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=\ngithub.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=\ngithub.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 h1:4Ew88p5s9dwIk5/woUyqI9BD89NgZoUNH4/rM/h2UDg=\ngithub.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw=\ngithub.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=\ngithub.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flimzy/diff v0.1.7 h1:DRbd+lN3lY1xVuQrfqvDNsqBwA6RMbClMs6tS5sqWWk=\ngithub.com/flimzy/diff v0.1.7/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=\ngithub.com/flimzy/testy v0.1.17 h1:Y+TUugY6s4B/vrOEPo6SUKafc41W5aiX3qUWvhAPMdI=\ngithub.com/flimzy/testy v0.1.17/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM=\ngithub.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=\ngithub.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=\ngithub.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/fsouza/go-dockerclient v1.11.0 h1:4ZAk6W7rPAtPXm7198EFqA5S68rwnNQORxlOA5OurCA=\ngithub.com/fsouza/go-dockerclient v1.11.0/go.mod h1:0I3TQCRseuPTzqlY4Y3ajfsg2VAdMQoazrkxJTiJg8s=\ngithub.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=\ngithub.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=\ngithub.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=\ngithub.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=\ngithub.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=\ngithub.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=\ngithub.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=\ngithub.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=\ngithub.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-kivik/couchdb v2.0.0+incompatible h1:DsXVuGJTng04Guz8tg7jGVQ53RlByEhk+gPB/1yo3Oo=\ngithub.com/go-kivik/couchdb v2.0.0+incompatible/go.mod h1:5XJRkAMpBlEVA4q0ktIZjUPYBjoBmRoiWvwUBzP3BOQ=\ngithub.com/go-kivik/kivik v2.0.0+incompatible h1:/7hgr29DKv/vlaJsUoyRlOFq0K+3ikz0wTbu+cIs7QY=\ngithub.com/go-kivik/kivik v2.0.0+incompatible/go.mod h1:nIuJ8z4ikBrVUSk3Ua8NoDqYKULPNjuddjqRvlSUyyQ=\ngithub.com/go-kivik/kiviktest v2.0.0+incompatible h1:y1RyPHqWQr+eFlevD30Tr3ipiPCxK78vRoD3o9YysjI=\ngithub.com/go-kivik/kiviktest v2.0.0+incompatible/go.mod h1:JdhVyzixoYhoIDUt6hRf1yAfYyaDa5/u9SDOindDkfQ=\ngithub.com/go-kivik/pouchdb v2.0.1+incompatible h1:v3OWB0/56qXjdlmu9az76nJ5utlbsHyRuB3uRq4sWFg=\ngithub.com/go-kivik/pouchdb v2.0.1+incompatible/go.mod h1:U+siUrqLCVxeMU3QjQTYIC3/F/e6EUKm+o5buJb7vpw=\ngithub.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=\ngithub.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=\ngithub.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=\ngithub.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=\ngithub.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=\ngithub.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=\ngithub.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=\ngithub.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=\ngithub.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=\ngithub.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=\ngithub.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=\ngithub.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20220221023154-0b2280d3ff96 h1:QJq7UBOuoynsywLk+aC75rC2Cbi2+lQRDaLaizhA+fA=\ngithub.com/gopherjs/gopherjs v0.0.0-20220221023154-0b2280d3ff96/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=\ngithub.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127 h1:atBEgNR1C5+LFkl8ipQtLee9RStheS8YeCSkiYqBhOg=\ngithub.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127/go.mod h1:7X1acUyFRf+oVFTU6SWw9mnb57Vxn+Nbh8iPbKg95hs=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=\ngithub.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=\ngithub.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hidal-go/hidalgo v0.3.0 h1:z74GAu6SGbznyEcLbsiFpsFNkcQcSZnKIXdn9Wz85Ek=\ngithub.com/hidal-go/hidalgo v0.3.0/go.mod h1:N85Y5d1N68Y1gJ3SYyQ/wHbMdHDZ/yH8Bp3hht1iuPY=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=\ngithub.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=\ngithub.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=\ngithub.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=\ngithub.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=\ngithub.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=\ngithub.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=\ngithub.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=\ngithub.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=\ngithub.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=\ngithub.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=\ngithub.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=\ngithub.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=\ngithub.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=\ngithub.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=\ngithub.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=\ngithub.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=\ngithub.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=\ngithub.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=\ngithub.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=\ngithub.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=\ngithub.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=\ngithub.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=\ngithub.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=\ngithub.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=\ngithub.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=\ngithub.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=\ngithub.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=\ngithub.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=\ngithub.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=\ngithub.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=\ngithub.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=\ngithub.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=\ngithub.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=\ngithub.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=\ngithub.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=\ngithub.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=\ngithub.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=\ngithub.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=\ngithub.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=\ngithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=\ngithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=\ngithub.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=\ngithub.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=\ngithub.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=\ngithub.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=\ngithub.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/olivere/elastic/v7 v7.0.12/go.mod h1:14rWX28Pnh3qCKYRVnSGXWLf9MbLonYS/4FDCY3LAPo=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=\ngithub.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=\ngithub.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=\ngithub.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs=\ngithub.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=\ngithub.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=\ngithub.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY=\ngithub.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=\ngithub.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=\ngithub.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=\ngithub.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=\ngithub.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=\ngithub.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=\ngithub.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=\ngithub.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=\ngithub.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM=\ngithub.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=\ngithub.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=\ngithub.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=\ngithub.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=\ngithub.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=\ngithub.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=\ngithub.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=\ngithub.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=\ngithub.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=\ngithub.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=\ngithub.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=\ngithub.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=\ngithub.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=\ngithub.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/gunit v1.1.3/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=\ngithub.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=\ngithub.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=\ngithub.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=\ngithub.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=\ngithub.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=\ngithub.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=\ngithub.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=\ngithub.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 h1:QEePdg0ty2r0t1+qwfZmQ4OOl/MB2UXIeJSpIZv56lg=\ngithub.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=\ngithub.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=\ngithub.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=\ngithub.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=\ngithub.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=\ngithub.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=\ngithub.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=\ngithub.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=\ngithub.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=\ngithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngitlab.com/flimzy/testy v0.10.2 h1:GQq5CtJK0+ph2c2cD/FAzvTz/OJbxjpMr6Kaf/UerHg=\ngitlab.com/flimzy/testy v0.10.2/go.mod h1:tcu652e6AyD5wS8q2JRUI+j5SlwIYsl3yq3ulHyuh8M=\ngo.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=\ngo.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=\ngo.mongodb.org/mongo-driver v1.8.4 h1:NruvZPPL0PBcRJKmbswoWSrmHeUvzdxA3GCPfD/NEOA=\ngo.mongodb.org/mongo-driver v1.8.4/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=\ngo.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=\ngolang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=\ngolang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=\ngolang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=\ngolang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=\ngolang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=\ngolang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=\ngolang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=\ngolang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=\ngoogle.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=\ngoogle.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=\ngoogle.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=\ngoogle.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=\ngopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=\ngopkg.in/olivere/elastic.v5 v5.0.86 h1:xFy6qRCGAmo5Wjx96srho9BitLhZl2fcnpuidPwduXM=\ngopkg.in/olivere/elastic.v5 v5.0.86/go.mod h1:M3WNlsF+WhYn7api4D87NIflwTV/c0iVs8cqfWhK+68=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=\ngotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\ngotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=\ngotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "gogen.go",
    "content": "package cayley\n\n//go:generate go run ./cmd/docgen/docgen.go -i ./docs/GizmoAPI.md.in -o ./docs/GizmoAPI.md\n"
  },
  {
    "path": "graph/all/all.go",
    "content": "package all\n\nimport (\n\t// supported backends\n\t_ \"github.com/cayleygraph/cayley/graph/kv/all\"\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\t_ \"github.com/cayleygraph/cayley/graph/nosql/all\"\n\t_ \"github.com/cayleygraph/cayley/graph/sql/cockroach\"\n\t_ \"github.com/cayleygraph/cayley/graph/sql/mysql\"\n\t_ \"github.com/cayleygraph/cayley/graph/sql/postgres\"\n)\n"
  },
  {
    "path": "graph/all/all_cgo.go",
    "content": "//go:build cgo\n\npackage all\n\nimport (\n\t// backends requiring cgo\n\t_ \"github.com/cayleygraph/cayley/graph/sql/sqlite\"\n)\n"
  },
  {
    "path": "graph/gaedatastore/config.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gaedatastore\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// Config defines the behavior of cayley database instances.\ntype Config struct {\n\tDatabaseType       string\n\tDatabasePath       string\n\tDatabaseOptions    map[string]interface{}\n\tReplicationType    string\n\tReplicationOptions map[string]interface{}\n\tListenHost         string\n\tListenPort         string\n\tReadOnly           bool\n\tTimeout            time.Duration\n\tLoadSize           int\n}\n\ntype config struct {\n\tDatabaseType       string                 `json:\"database\"`\n\tDatabasePath       string                 `json:\"db_path\"`\n\tDatabaseOptions    map[string]interface{} `json:\"db_options\"`\n\tReplicationType    string                 `json:\"replication\"`\n\tReplicationOptions map[string]interface{} `json:\"replication_options\"`\n\tListenHost         string                 `json:\"listen_host\"`\n\tListenPort         string                 `json:\"listen_port\"`\n\tReadOnly           bool                   `json:\"read_only\"`\n\tTimeout            duration               `json:\"timeout\"`\n\tLoadSize           int                    `json:\"load_size\"`\n}\n\nfunc (c *Config) UnmarshalJSON(data []byte) error {\n\tvar t config\n\terr := json.Unmarshal(data, &t)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*c = Config{\n\t\tDatabaseType:       t.DatabaseType,\n\t\tDatabasePath:       t.DatabasePath,\n\t\tDatabaseOptions:    t.DatabaseOptions,\n\t\tReplicationType:    t.ReplicationType,\n\t\tReplicationOptions: t.ReplicationOptions,\n\t\tListenHost:         t.ListenHost,\n\t\tListenPort:         t.ListenPort,\n\t\tReadOnly:           t.ReadOnly,\n\t\tTimeout:            time.Duration(t.Timeout),\n\t\tLoadSize:           t.LoadSize,\n\t}\n\treturn nil\n}\n\nfunc (c *Config) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(config{\n\t\tDatabaseType:       c.DatabaseType,\n\t\tDatabasePath:       c.DatabasePath,\n\t\tDatabaseOptions:    c.DatabaseOptions,\n\t\tReplicationType:    c.ReplicationType,\n\t\tReplicationOptions: c.ReplicationOptions,\n\t\tListenHost:         c.ListenHost,\n\t\tListenPort:         c.ListenPort,\n\t\tReadOnly:           c.ReadOnly,\n\t\tTimeout:            duration(c.Timeout),\n\t\tLoadSize:           c.LoadSize,\n\t})\n}\n\n// duration is a time.Duration that satisfies the\n// json.UnMarshaler and json.Marshaler interfaces.\ntype duration time.Duration\n\n// UnmarshalJSON unmarshals a duration according to the following scheme:\n//  * If the element is absent the duration is zero.\n//  * If the element is parsable as a time.Duration, the parsed value is kept.\n//  * If the element is parsable as a number, that number of seconds is kept.\nfunc (d *duration) UnmarshalJSON(data []byte) error {\n\tif len(data) == 0 {\n\t\t*d = 0\n\t\treturn nil\n\t}\n\ttext := string(data)\n\tt, err := time.ParseDuration(text)\n\tif err == nil {\n\t\t*d = duration(t)\n\t\treturn nil\n\t}\n\ti, err := strconv.ParseInt(text, 10, 64)\n\tif err == nil {\n\t\t*d = duration(time.Duration(i) * time.Second)\n\t\treturn nil\n\t}\n\t// This hack is to get around strconv.ParseFloat\n\t// not handling e-notation for integers.\n\tf, err := strconv.ParseFloat(text, 64)\n\t*d = duration(time.Duration(f) * time.Second)\n\treturn err\n}\n\nfunc (d *duration) MarshalJSON() ([]byte, error) {\n\treturn []byte(fmt.Sprintf(\"%q\", *d)), nil\n}\n\n// LoadConf reads a JSON-encoded config contained in the given file. A zero value\n// config is returned if the filename is empty.\nfunc LoadConf(file string) (*Config, error) {\n\tconfig := &Config{}\n\tif file == \"\" {\n\t\treturn config, nil\n\t}\n\tf, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not open config file %q: %v\", file, err)\n\t}\n\tdefer f.Close()\n\n\tdec := json.NewDecoder(f)\n\terr = dec.Decode(config)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse config file %q: %v\", file, err)\n\t}\n\treturn config, nil\n}\n"
  },
  {
    "path": "graph/gaedatastore/iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gaedatastore\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n\n\t\"google.golang.org/appengine/datastore\"\n)\n\nvar _ iterator.Shape = &Iterator{}\n\nconst (\n\tbufferSize = 50\n)\n\ntype Iterator struct {\n\tsize  int64\n\tdir   quad.Direction\n\tqs    *QuadStore\n\tt     *Token\n\tisAll bool\n\tkind  string\n}\n\nfunc (it *Iterator) Iterate() iterator.Scanner {\n\tif it.isAll {\n\t\treturn newAllIteratorNext(it.qs, it.kind)\n\t}\n\treturn newIteratorNext(it.qs, it.kind, it.dir, it.t)\n}\n\nfunc (it *Iterator) Lookup() iterator.Index {\n\tif it.isAll {\n\t\treturn newAllIteratorContains(it.qs, it.kind)\n\t}\n\treturn newIteratorContains(it.qs, it.kind, it.dir, it.t)\n}\n\nfunc (qs *QuadStore) newIterator(k string, d quad.Direction, val graph.Ref) *Iterator {\n\tt := val.(*Token)\n\tif t == nil {\n\t\tclog.Errorf(\"Token == nil\")\n\t}\n\treturn &Iterator{\n\t\tdir:   d,\n\t\tqs:    qs,\n\t\tisAll: false,\n\t\tt:     t,\n\t\tkind:  k,\n\t}\n}\n\nfunc (qs *QuadStore) newAllIterator(kind string) *Iterator {\n\treturn &Iterator{\n\t\tqs:    qs,\n\t\tdir:   quad.Any,\n\t\tisAll: true,\n\t\tkind:  kind,\n\t}\n}\n\n// No subiterators.\nfunc (it *Iterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *Iterator) Sorted() bool                                        { return false }\nfunc (it *Iterator) Optimize(ctx context.Context) (iterator.Shape, bool) { return it, false }\nfunc (it *Iterator) String() string {\n\tname := \"\"\n\tif it.t != nil {\n\t\ttn, err := it.qs.NameOf(it.t)\n\t\tif err != nil {\n\t\t\tname = \"ERROR(\" + err.Error() + \")\"\n\t\t} else {\n\t\t\tname = quad.StringOf(tn) + \"/\" + it.t.Hash\n\t\t}\n\t}\n\treturn fmt.Sprintf(\"GAE(%s)\", name)\n}\n\nfunc (it *Iterator) getSize(ctx context.Context) (refs.Size, error) {\n\tif it.size != 0 {\n\t\treturn refs.Size{\n\t\t\tValue: it.size,\n\t\t\tExact: true,\n\t\t}, nil\n\t}\n\tif !it.isAll {\n\t\t// The number of references to this node is held in the nodes entity\n\t\tkey := it.qs.createKeyFromToken(it.t)\n\t\tfoundNode := new(NodeEntry)\n\t\terr := datastore.Get(it.qs.context, key, foundNode)\n\t\tif err != datastore.ErrNoSuchEntity {\n\t\t\terr = nil\n\t\t}\n\t\tsize := foundNode.Size\n\t\tit.size = size\n\t\treturn refs.Size{\n\t\t\tValue: it.size,\n\t\t\tExact: err == nil,\n\t\t}, err\n\t}\n\tvar size int64\n\tst, err := it.qs.Stats(context.Background(), true)\n\tif it.kind == nodeKind {\n\t\tsize = st.Nodes.Value\n\t} else {\n\t\tsize = st.Quads.Value\n\t}\n\tit.size = size\n\treturn refs.Size{\n\t\tValue: it.size,\n\t\tExact: err == nil,\n\t}, err\n}\n\nfunc (it *Iterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\tsz, err := it.getSize(ctx)\n\t// TODO (panamafrancis) calculate costs\n\treturn iterator.Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     5,\n\t\tSize:         sz,\n\t}, err\n}\n\ntype iteratorNext struct {\n\tdir    quad.Direction\n\tqs     *QuadStore\n\tname   string\n\tisAll  bool\n\tkind   string\n\thash   string\n\tdone   bool\n\tbuffer []string\n\toffset int\n\tlast   string\n\tresult graph.Ref\n\terr    error\n}\n\nfunc newIteratorNext(qs *QuadStore, k string, d quad.Direction, t *Token) *iteratorNext {\n\tif t == nil {\n\t\tclog.Errorf(\"Token == nil\")\n\t}\n\tif t.Kind != nodeKind {\n\t\tclog.Errorf(\"Cannot create an iterator from a non-node value\")\n\t\treturn &iteratorNext{done: true}\n\t}\n\tif k != nodeKind && k != quadKind {\n\t\tclog.Errorf(\"Cannot create iterator for unknown kind\")\n\t\treturn &iteratorNext{done: true}\n\t}\n\tif qs.context == nil {\n\t\tclog.Errorf(\"Cannot create iterator without a valid context\")\n\t\treturn &iteratorNext{done: true}\n\t}\n\ttn, err := qs.NameOf(t)\n\tif err != nil {\n\t\tclog.Errorf(\"Creating iterator token lookup error: %v\", err)\n\t\treturn &iteratorNext{done: true, err: err}\n\t}\n\tname := quad.StringOf(tn)\n\treturn &iteratorNext{\n\t\tname:  name,\n\t\tdir:   d,\n\t\tqs:    qs,\n\t\tisAll: false,\n\t\tkind:  k,\n\t\thash:  t.Hash,\n\t\tdone:  false,\n\t}\n}\n\nfunc newAllIteratorNext(qs *QuadStore, kind string) *iteratorNext {\n\tif kind != nodeKind && kind != quadKind {\n\t\tclog.Errorf(\"Cannot create iterator for an unknown kind\")\n\t\treturn &iteratorNext{done: true}\n\t}\n\tif qs.context == nil {\n\t\tclog.Errorf(\"Cannot create iterator without a valid context\")\n\t\treturn &iteratorNext{done: true}\n\t}\n\treturn &iteratorNext{\n\t\tqs:    qs,\n\t\tdir:   quad.Any,\n\t\tisAll: true,\n\t\tkind:  kind,\n\t}\n}\n\nfunc (it *iteratorNext) Close() error {\n\tit.buffer = nil\n\tit.offset = 0\n\tit.done = true\n\tit.last = \"\"\n\tit.result = nil\n\treturn nil\n}\n\nfunc (it *iteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorNext) Result() graph.Ref {\n\treturn it.result\n}\n\nfunc (it *iteratorNext) Next(ctx context.Context) bool {\n\tif it.offset+1 < len(it.buffer) {\n\t\tit.offset++\n\t\tit.result = &Token{Kind: it.kind, Hash: it.buffer[it.offset]}\n\t\treturn true\n\t}\n\tif it.done {\n\t\treturn false\n\t}\n\t// Reset buffer and offset\n\tit.offset = 0\n\tit.buffer = make([]string, 0, bufferSize)\n\t// Create query\n\t// TODO (panamafrancis) Keys only query?\n\tq := datastore.NewQuery(it.kind).Limit(bufferSize)\n\tif !it.isAll {\n\t\t// Filter on the direction {subject,objekt...}\n\t\tq = q.Filter(it.dir.String()+\" =\", it.name)\n\t}\n\t// Get last cursor position\n\tcursor, err := datastore.DecodeCursor(it.last)\n\tif err == nil {\n\t\tq = q.Start(cursor)\n\t}\n\t// Buffer the keys of the next 50 matches\n\tt := q.Run(it.qs.context)\n\tfor {\n\t\t// Quirk of the datastore, you cannot pass a nil value to to Next()\n\t\t// even if you just want the keys\n\t\tvar k *datastore.Key\n\t\tskip := false\n\t\tif it.kind == quadKind {\n\t\t\ttemp := new(QuadEntry)\n\t\t\tk, err = t.Next(temp)\n\t\t\t// Skip if quad has been deleted\n\t\t\tif len(temp.Added) <= len(temp.Deleted) {\n\t\t\t\tskip = true\n\t\t\t}\n\t\t} else {\n\t\t\ttemp := new(NodeEntry)\n\t\t\tk, err = t.Next(temp)\n\t\t\t// Skip if node has been deleted\n\t\t\tif temp.Size == 0 {\n\t\t\t\tskip = true\n\t\t\t}\n\t\t}\n\t\tif err == datastore.Done {\n\t\t\tit.done = true\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"Error fetching next entry %v\", err)\n\t\t\tit.err = err\n\t\t\treturn false\n\t\t}\n\t\tif !skip {\n\t\t\tit.buffer = append(it.buffer, k.StringID())\n\t\t}\n\t}\n\t// Save cursor position\n\tcursor, err = t.Cursor()\n\tif err == nil {\n\t\tit.last = cursor.String()\n\t}\n\t// Protect against bad queries\n\tif it.done && len(it.buffer) == 0 {\n\t\tclog.Warningf(\"Query did not return any results\")\n\t\treturn false\n\t}\n\t// First result\n\tit.result = &Token{Kind: it.kind, Hash: it.buffer[it.offset]}\n\treturn true\n}\n\nfunc (it *iteratorNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorNext) Sorted() bool { return false }\nfunc (it *iteratorNext) String() string {\n\treturn fmt.Sprintf(\"GAE(%s/%s)\", it.name, it.hash)\n}\n\ntype iteratorContains struct {\n\tdir    quad.Direction\n\tqs     *QuadStore\n\tname   string\n\tisAll  bool\n\tkind   string\n\thash   string\n\tdone   bool\n\tresult graph.Ref\n\terr    error\n}\n\nfunc newIteratorContains(qs *QuadStore, k string, d quad.Direction, t *Token) *iteratorContains {\n\tif t == nil {\n\t\tclog.Errorf(\"Token == nil\")\n\t}\n\tif t.Kind != nodeKind {\n\t\tclog.Errorf(\"Cannot create an iterator from a non-node value\")\n\t\treturn &iteratorContains{done: true}\n\t}\n\tif k != nodeKind && k != quadKind {\n\t\tclog.Errorf(\"Cannot create iterator for unknown kind\")\n\t\treturn &iteratorContains{done: true}\n\t}\n\tif qs.context == nil {\n\t\tclog.Errorf(\"Cannot create iterator without a valid context\")\n\t\treturn &iteratorContains{done: true}\n\t}\n\ttn, err := qs.NameOf(t)\n\tif err != nil {\n\t\tclog.Errorf(\"Creating iterator token lookup error: %v\", err)\n\t\treturn &iteratorContains{done: true, err: err}\n\t}\n\tname := quad.StringOf(tn)\n\treturn &iteratorContains{\n\t\tname:  name,\n\t\tdir:   d,\n\t\tqs:    qs,\n\t\tisAll: false,\n\t\tkind:  k,\n\t\thash:  t.Hash,\n\t\tdone:  false,\n\t}\n}\n\nfunc newAllIteratorContains(qs *QuadStore, kind string) *iteratorContains {\n\tif kind != nodeKind && kind != quadKind {\n\t\tclog.Errorf(\"Cannot create iterator for an unknown kind\")\n\t\treturn &iteratorContains{done: true}\n\t}\n\tif qs.context == nil {\n\t\tclog.Errorf(\"Cannot create iterator without a valid context\")\n\t\treturn &iteratorContains{done: true}\n\t}\n\treturn &iteratorContains{\n\t\tqs:    qs,\n\t\tdir:   quad.Any,\n\t\tisAll: true,\n\t\tkind:  kind,\n\t}\n}\n\nfunc (it *iteratorContains) Close() error {\n\tit.done = true\n\tit.result = nil\n\treturn nil\n}\n\nfunc (it *iteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tif it.isAll {\n\t\t// The result needs to be set, so when contains is called, the result can be retrieved\n\t\tit.result = v\n\t\treturn true\n\t}\n\tt := v.(*Token)\n\tif t == nil {\n\t\tclog.Errorf(\"Could not cast to token\")\n\t\treturn false\n\t}\n\tif t.Kind == nodeKind {\n\t\tclog.Errorf(\"Contains does not work with node values\")\n\t\treturn false\n\t}\n\t// Contains is for when you want to know that an iterator refers to a quad\n\tvar offset int\n\tswitch it.dir {\n\tcase quad.Subject:\n\t\toffset = 0\n\tcase quad.Predicate:\n\t\toffset = (quad.HashSize * 2)\n\tcase quad.Object:\n\t\toffset = (quad.HashSize * 2) * 2\n\tcase quad.Label:\n\t\toffset = (quad.HashSize * 2) * 3\n\t}\n\tval := t.Hash[offset : offset+(quad.HashSize*2)]\n\tif val == it.hash {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *iteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorContains) Result() graph.Ref {\n\treturn it.result\n}\n\nfunc (it *iteratorContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorContains) Sorted() bool { return false }\nfunc (it *iteratorContains) String() string {\n\treturn fmt.Sprintf(\"GAE(%s/%s)\", it.name, it.hash)\n}\n"
  },
  {
    "path": "graph/gaedatastore/quadstore.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gaedatastore\n\nimport (\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"math\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"golang.org/x/net/context\"\n\t\"google.golang.org/appengine\"\n\t\"google.golang.org/appengine/datastore\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\thttpgraph \"github.com/cayleygraph/cayley/graph/http\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar _ httpgraph.QuadStore = (*QuadStore)(nil)\n\nconst (\n\tQuadStoreType = \"gaedatastore\"\n\tquadKind      = \"quad\"\n\tnodeKind      = \"node\"\n)\n\nvar (\n\t// Order of quad fields\n\tspo = [4]quad.Direction{quad.Subject, quad.Predicate, quad.Object, quad.Label}\n)\n\ntype QuadStore struct {\n\tcontext context.Context\n}\n\ntype MetadataEntry struct {\n\tNodeCount int64\n\tQuadCount int64\n}\n\ntype Token struct {\n\tKind string\n\tHash string\n}\n\nfunc (t Token) IsNode() bool     { return t.Kind == nodeKind }\nfunc (t Token) Key() interface{} { return t }\n\ntype QuadEntry struct {\n\tHash      string\n\tAdded     []int64 `datastore:\",noindex\"`\n\tDeleted   []int64 `datastore:\",noindex\"`\n\tSubject   string  `datastore:\"subject\"`\n\tPredicate string  `datastore:\"predicate\"`\n\tObject    string  `datastore:\"object\"`\n\tLabel     string  `datastore:\"label\"`\n}\n\ntype NodeEntry struct {\n\tName string\n\tSize int64\n}\n\ntype LogEntry struct {\n\tAction    string\n\tKey       string\n\tTimestamp int64\n}\n\nfunc init() {\n\tgraph.RegisterQuadStore(\"gaedatastore\", graph.QuadStoreRegistration{\n\t\tNewFunc:      newQuadStore,\n\t\tUpgradeFunc:  nil,\n\t\tInitFunc:     initQuadStore,\n\t\tIsPersistent: true,\n\t})\n}\n\nfunc initQuadStore(_ string, _ graph.Options) error {\n\t// TODO (panamafrancis) check appengine datastore for consistency\n\treturn nil\n}\n\nfunc newQuadStore(_ string, options graph.Options) (graph.QuadStore, error) {\n\treturn &QuadStore{}, nil\n}\n\nfunc (qs *QuadStore) createKeyForQuad(q quad.Quad) *datastore.Key {\n\tid := hashOf(q.Subject)\n\tid += hashOf(q.Predicate)\n\tid += hashOf(q.Object)\n\tid += hashOf(q.Label)\n\treturn qs.createKeyFromToken(&Token{quadKind, id})\n}\n\nfunc hashOf(s quad.Value) string {\n\treturn hex.EncodeToString(quad.HashOf(s))\n}\n\nfunc (qs *QuadStore) createKeyForNode(n quad.Value) *datastore.Key {\n\tid := hashOf(n)\n\treturn qs.createKeyFromToken(&Token{nodeKind, id})\n}\n\nfunc (qs *QuadStore) createKeyForMetadata() *datastore.Key {\n\treturn qs.createKeyFromToken(&Token{\"metadata\", \"metadataentry\"})\n}\n\nfunc (qs *QuadStore) createKeyForLog() *datastore.Key {\n\treturn datastore.NewKey(qs.context, \"logentry\", \"\", 0, nil)\n}\n\nfunc (qs *QuadStore) createKeyFromToken(t *Token) *datastore.Key {\n\treturn datastore.NewKey(qs.context, t.Kind, t.Hash, 0, nil)\n}\n\nfunc (qs *QuadStore) checkValid(k *datastore.Key) (bool, error) {\n\tvar q QuadEntry\n\terr := datastore.Get(qs.context, k, &q)\n\tif err == datastore.ErrNoSuchEntity {\n\t\treturn false, nil\n\t}\n\tif _, ok := err.(*datastore.ErrFieldMismatch); ok {\n\t\treturn true, nil\n\t}\n\tif err != nil {\n\t\tclog.Warningf(\"Error occurred when getting quad/node %s %v\", k, err)\n\t\treturn false, err\n\t}\n\t// a deleted node should not be returned as found.\n\tif len(q.Deleted) >= len(q.Added) {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\n\nfunc getContext(opts graph.Options) (context.Context, error) {\n\treq := opts[\"HTTPRequest\"].(*http.Request)\n\tif req == nil {\n\t\terr := errors.New(\"HTTP Request needed\")\n\t\tclog.Errorf(\"%v\", err)\n\t\treturn nil, err\n\t}\n\treturn appengine.NewContext(req), nil\n}\n\nfunc (qs *QuadStore) ForRequest(r *http.Request) (graph.QuadStore, error) {\n\treturn &QuadStore{context: appengine.NewContext(r)}, nil\n}\n\nfunc (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn &quadWriter{qs: qs}, nil\n}\n\ntype quadWriter struct {\n\tqs     *QuadStore\n\tdeltas []graph.Delta\n}\n\nfunc (w *quadWriter) WriteQuad(q quad.Quad) error {\n\t_, err := w.WriteQuads([]quad.Quad{q})\n\treturn err\n}\n\nfunc (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\t// TODO(dennwc): write an optimized implementation\n\tw.deltas = w.deltas[:0]\n\tif cap(w.deltas) < len(buf) {\n\t\tw.deltas = make([]graph.Delta, 0, len(buf))\n\t}\n\tfor _, q := range buf {\n\t\tw.deltas = append(w.deltas, graph.Delta{\n\t\t\tQuad: q, Action: graph.Add,\n\t\t})\n\t}\n\terr := w.qs.ApplyDeltas(w.deltas, graph.IgnoreOpts{\n\t\tIgnoreDup: true,\n\t})\n\tw.deltas = w.deltas[:0]\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\tw.deltas = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) ApplyDeltas(in []graph.Delta, ignoreOpts graph.IgnoreOpts) error {\n\tif qs.context == nil {\n\t\treturn errors.New(\"No context, graph not correctly initialised\")\n\t}\n\ttoKeep := make([]graph.Delta, 0)\n\tfor _, d := range in {\n\t\tif d.Action != graph.Add && d.Action != graph.Delete {\n\t\t\t//Defensive shortcut\n\t\t\treturn errors.New(\"Datastore: invalid action\")\n\t\t}\n\t\tkey := qs.createKeyForQuad(d.Quad)\n\t\tkeep := false\n\t\tswitch d.Action {\n\t\tcase graph.Add:\n\t\t\tfound, err := qs.checkValid(key)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif found {\n\t\t\t\tif !ignoreOpts.IgnoreDup {\n\t\t\t\t\treturn graph.ErrQuadExists\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tkeep = true\n\t\t\t}\n\t\tcase graph.Delete:\n\t\t\tfound, err := qs.checkValid(key)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif found || ignoreOpts.IgnoreMissing {\n\t\t\t\tkeep = true\n\t\t\t} else {\n\t\t\t\treturn graph.ErrQuadNotExist\n\t\t\t}\n\t\tdefault:\n\t\t\tkeep = false\n\t\t}\n\t\tif keep {\n\t\t\ttoKeep = append(toKeep, d)\n\t\t}\n\t}\n\tif len(toKeep) == 0 {\n\t\treturn nil\n\t}\n\tids, err := qs.updateLog(toKeep)\n\tif err != nil {\n\t\tclog.Errorf(\"Updating log failed %v\", err)\n\t\treturn err\n\t}\n\n\tif clog.V(2) {\n\t\tclog.Infof(\"Existence verified. Proceeding.\")\n\t}\n\n\tquadsAdded, err := qs.updateQuads(toKeep, ids)\n\tif err != nil {\n\t\tclog.Errorf(\"UpdateQuads failed %v\", err)\n\t\treturn err\n\t}\n\tnodesAdded, err := qs.updateNodes(toKeep)\n\tif err != nil {\n\t\tclog.Warningf(\"UpdateNodes failed %v\", err)\n\t\treturn err\n\t}\n\terr = qs.updateMetadata(quadsAdded, nodesAdded)\n\tif err != nil {\n\t\tclog.Warningf(\"UpdateMetadata failed %v\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) updateNodes(in []graph.Delta) (int64, error) {\n\t// Collate changes to each node\n\tvar countDelta int64\n\tvar nodesAdded int64\n\tnodeDeltas := make(map[quad.Value]int64)\n\tfor _, d := range in {\n\t\tif d.Action == graph.Add {\n\t\t\tcountDelta = 1\n\t\t} else {\n\t\t\tcountDelta = -1\n\t\t}\n\t\tnodeDeltas[d.Quad.Subject] += countDelta\n\t\tnodeDeltas[d.Quad.Object] += countDelta\n\t\tnodeDeltas[d.Quad.Predicate] += countDelta\n\t\tif d.Quad.Label != nil {\n\t\t\tnodeDeltas[d.Quad.Label] += countDelta\n\t\t}\n\t\tnodesAdded += countDelta\n\t}\n\t// Create keys and new nodes\n\tkeys := make([]*datastore.Key, 0, len(nodeDeltas))\n\ttempNodes := make([]NodeEntry, 0, len(nodeDeltas))\n\tfor k, v := range nodeDeltas {\n\t\tkeys = append(keys, qs.createKeyForNode(k))\n\t\ttempNodes = append(tempNodes, NodeEntry{k.String(), v})\n\t}\n\t// In accordance with the appengine datastore spec, cross group transactions\n\t// like these can only be done in batches of 5\n\tfor i := 0; i < len(nodeDeltas); i += 5 {\n\t\tj := int(math.Min(float64(len(nodeDeltas)-i), 5))\n\t\tfoundNodes := make([]NodeEntry, j)\n\t\terr := datastore.RunInTransaction(qs.context, func(c context.Context) error {\n\t\t\terr := datastore.GetMulti(c, keys[i:i+j], foundNodes)\n\t\t\t// Sift through for errors\n\t\t\tif me, ok := err.(appengine.MultiError); ok {\n\t\t\t\tfor _, merr := range me {\n\t\t\t\t\tif merr != nil && merr != datastore.ErrNoSuchEntity {\n\t\t\t\t\t\tclog.Errorf(\"Error: %v\", merr)\n\t\t\t\t\t\treturn merr\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Carry forward the sizes of the nodes from the datastore\n\t\t\tfor k := range foundNodes {\n\t\t\t\tif foundNodes[k].Name != \"\" {\n\t\t\t\t\ttempNodes[i+k].Size += foundNodes[k].Size\n\t\t\t\t}\n\t\t\t}\n\t\t\t_, err = datastore.PutMulti(c, keys[i:i+j], tempNodes[i:i+j])\n\t\t\treturn err\n\t\t}, &datastore.TransactionOptions{XG: true})\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"Error: %v\", err)\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\treturn nodesAdded, nil\n}\n\nfunc (qs *QuadStore) updateQuads(in []graph.Delta, ids []int64) (int64, error) {\n\tkeys := make([]*datastore.Key, 0, len(in))\n\tfor _, d := range in {\n\t\tkeys = append(keys, qs.createKeyForQuad(d.Quad))\n\t}\n\tvar quadCount int64\n\tfor i := 0; i < len(in); i += 5 {\n\t\t// Find the closest batch of 5\n\t\tj := int(math.Min(float64(len(in)-i), 5))\n\t\terr := datastore.RunInTransaction(qs.context, func(c context.Context) error {\n\t\t\tfoundQuads := make([]QuadEntry, j)\n\t\t\t// We don't process errors from GetMulti as they don't mean anything,\n\t\t\t// we've handled existing quad conflicts above and we overwrite everything again anyways\n\t\t\tdatastore.GetMulti(c, keys, foundQuads)\n\t\t\tfor k := range foundQuads {\n\t\t\t\tx := i + k\n\t\t\t\tfoundQuads[k].Hash = keys[x].StringID()\n\t\t\t\tfoundQuads[k].Subject = in[x].Quad.Subject.String()\n\t\t\t\tfoundQuads[k].Predicate = in[x].Quad.Predicate.String()\n\t\t\t\tfoundQuads[k].Object = in[x].Quad.Object.String()\n\t\t\t\tfoundQuads[k].Label = quad.StringOf(in[x].Quad.Label)\n\n\t\t\t\t// If the quad exists the Added[] will be non-empty\n\t\t\t\tif in[x].Action == graph.Add {\n\t\t\t\t\tfoundQuads[k].Added = append(foundQuads[k].Added, ids[x])\n\t\t\t\t\tquadCount++\n\t\t\t\t} else {\n\t\t\t\t\tfoundQuads[k].Deleted = append(foundQuads[k].Deleted, ids[x])\n\t\t\t\t\tquadCount--\n\t\t\t\t}\n\t\t\t}\n\t\t\t_, err := datastore.PutMulti(c, keys[i:i+j], foundQuads)\n\t\t\treturn err\n\t\t}, &datastore.TransactionOptions{XG: true})\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn quadCount, nil\n}\n\nfunc (qs *QuadStore) updateMetadata(quadsAdded int64, nodesAdded int64) error {\n\tkey := qs.createKeyForMetadata()\n\tfoundMetadata := new(MetadataEntry)\n\terr := datastore.RunInTransaction(qs.context, func(c context.Context) error {\n\t\terr := datastore.Get(c, key, foundMetadata)\n\t\tif err != nil && err != datastore.ErrNoSuchEntity {\n\t\t\tclog.Errorf(\"Error: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tfoundMetadata.QuadCount += quadsAdded\n\t\tfoundMetadata.NodeCount += nodesAdded\n\t\t_, err = datastore.Put(c, key, foundMetadata)\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"Error: %v\", err)\n\t\t}\n\t\treturn err\n\t}, nil)\n\treturn err\n}\n\nfunc (qs *QuadStore) updateLog(in []graph.Delta) ([]int64, error) {\n\tif qs.context == nil {\n\t\terr := errors.New(\"Error updating log, context is nil, graph not correctly initialised\")\n\t\treturn nil, err\n\t}\n\tif len(in) == 0 {\n\t\treturn nil, errors.New(\"Nothing to log\")\n\t}\n\tlogEntries := make([]LogEntry, 0, len(in))\n\tlogKeys := make([]*datastore.Key, 0, len(in))\n\tfor _, d := range in {\n\t\tvar action string\n\t\tif d.Action == graph.Add {\n\t\t\taction = \"Add\"\n\t\t} else {\n\t\t\taction = \"Delete\"\n\t\t}\n\n\t\tentry := LogEntry{\n\t\t\tAction:    action,\n\t\t\tKey:       qs.createKeyForQuad(d.Quad).String(),\n\t\t\tTimestamp: time.Now().UnixNano(),\n\t\t}\n\t\tlogEntries = append(logEntries, entry)\n\t\tlogKeys = append(logKeys, qs.createKeyForLog())\n\t}\n\n\tids, err := datastore.PutMulti(qs.context, logKeys, logEntries)\n\tif err != nil {\n\t\tclog.Errorf(\"Error updating log: %v\", err)\n\t\treturn nil, err\n\t}\n\tout := make([]int64, 0, len(ids))\n\tfor _, id := range ids {\n\t\tout = append(out, id.IntID())\n\t}\n\treturn out, nil\n}\n\nfunc (qs *QuadStore) QuadIterator(dir quad.Direction, v graph.Ref) iterator.Shape {\n\treturn qs.newIterator(quadKind, dir, v)\n}\n\nfunc (qs *QuadStore) NodesAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(nodeKind)\n}\n\nfunc (qs *QuadStore) QuadsAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(quadKind)\n}\n\nfunc (qs *QuadStore) ValueOf(s quad.Value) (graph.Ref, error) {\n\tid := hashOf(s)\n\treturn &Token{Kind: nodeKind, Hash: id}, nil\n}\n\nfunc (qs *QuadStore) NameOf(val graph.Ref) (quad.Value, error) {\n\tif qs.context == nil {\n\t\treturn nil, errors.New(\"context is nil, graph is not initialized\")\n\t} else if v, ok := val.(refs.PreFetchedValue); ok {\n\t\treturn v.NameOf(), nil\n\t}\n\tvar key *datastore.Key\n\tif t, ok := val.(*Token); ok && t.Kind == nodeKind {\n\t\tkey = qs.createKeyFromToken(t)\n\t} else {\n\t\treturn nil, errors.New(\"token not valid\")\n\t}\n\n\t// TODO (panamafrancis) implement a cache\n\n\tnode := new(NodeEntry)\n\terr := datastore.Get(qs.context, key, node)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn quad.Raw(node.Name), nil\n}\n\nfunc (qs *QuadStore) Quad(val graph.Ref) (quad.Quad, error) {\n\tif qs.context == nil {\n\t\treturn quad.Quad{}, errors.New(\"context is nil, graph is not correctly initialized\")\n\t}\n\tvar key *datastore.Key\n\tif t, ok := val.(*Token); ok && t.Kind == quadKind {\n\t\tkey = qs.createKeyFromToken(t)\n\t} else {\n\t\treturn quad.Quad{}, errors.New(\"gae quad: token not valid\")\n\t}\n\n\tq := new(QuadEntry)\n\terr := datastore.Get(qs.context, key, q)\n\tif err != nil {\n\t\t// Red herring error : ErrFieldMismatch can happen when a quad exists but a field is empty\n\t\tif _, ok := err.(*datastore.ErrFieldMismatch); !ok {\n\t\t\treturn quad.Quad{}, err\n\t\t}\n\t}\n\tvar label interface{}\n\tif q.Label != \"\" {\n\t\tlabel = q.Label\n\t}\n\treturn quad.Make(\n\t\tq.Subject,\n\t\tq.Predicate,\n\t\tq.Object,\n\t\tlabel,\n\t), nil\n}\n\nfunc (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\tif qs.context == nil {\n\t\treturn graph.Stats{}, errors.New(\"error fetching size, context is nil, graph not correctly initialised\")\n\t}\n\tkey := qs.createKeyForMetadata()\n\tm := new(MetadataEntry)\n\terr := datastore.Get(qs.context, key, m)\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\treturn graph.Stats{\n\t\tNodes: refs.Size{\n\t\t\tValue: m.NodeCount,\n\t\t\tExact: true,\n\t\t},\n\t\tQuads: refs.Size{\n\t\t\tValue: m.QuadCount,\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {\n\tt, ok := val.(*Token)\n\tif !ok || t.Kind != nodeKind {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t} else if qs.context == nil {\n\t\treturn refs.Size{}, errors.New(\"cannot count iterator without a valid context\")\n\t}\n\tkey := qs.createKeyFromToken(t)\n\tn := new(NodeEntry)\n\terr := datastore.Get(qs.context, key, n)\n\tif err != nil && err != datastore.ErrNoSuchEntity {\n\t\treturn refs.Size{}, err\n\t}\n\treturn refs.Size{Value: n.Size, Exact: true}, nil\n}\n\nfunc (qs *QuadStore) Close() error {\n\tqs.context = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) QuadDirection(val graph.Ref, dir quad.Direction) (graph.Ref, error) {\n\tt, ok := val.(*Token)\n\tif !ok {\n\t\treturn nil, errors.New(\"token not valid\")\n\t}\n\tif t.Kind == nodeKind {\n\t\treturn nil, errors.New(\"node tokens not valid\")\n\t}\n\tvar offset int\n\tswitch dir {\n\tcase quad.Subject:\n\t\toffset = 0\n\tcase quad.Predicate:\n\t\toffset = (quad.HashSize * 2)\n\tcase quad.Object:\n\t\toffset = (quad.HashSize * 2) * 2\n\tcase quad.Label:\n\t\toffset = (quad.HashSize * 2) * 3\n\t}\n\tsub := t.Hash[offset : offset+(quad.HashSize*2)]\n\treturn &Token{Kind: nodeKind, Hash: sub}, nil\n}\n"
  },
  {
    "path": "graph/gaedatastore/quadstore_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.  //\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// +build appengine appenginevm\n\npackage gaedatastore\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"google.golang.org/appengine/aetest\"\n)\n\n// This is a simple test graph.\n//\n//    +---+                        +---+\n//    | A |-------               ->| F |<--\n//    +---+       \\------>+---+-/  +---+   \\--+---+\n//                 ------>|#B#|      |        | E |\n//    +---+-------/      >+---+      |        +---+\n//    | C |             /            v\n//    +---+           -/           +---+\n//      ----    +---+/             |#G#|\n//          \\-->|#D#|------------->+---+\n//              +---+\n//\nvar simpleGraph = graphtest.MakeQuadSet()\nvar simpleGraphUpdate = []quad.Quad{\n\tquad.MakeRaw(\"A\", \"follows\", \"B\", \"\"),\n\tquad.MakeRaw(\"F\", \"follows\", \"B\", \"\"),\n\tquad.MakeRaw(\"C\", \"follows\", \"D\", \"\"),\n\tquad.MakeRaw(\"X\", \"follows\", \"B\", \"\"),\n}\n\ntype pair struct {\n\tquery string\n\tvalue int64\n}\n\nfunc createInstance() (aetest.Instance, *http.Request, error) {\n\tinst, err := aetest.NewInstance(&aetest.Options{\n\t\tAppID:                       \"\",\n\t\tStronglyConsistentDatastore: true,\n\t\tStartupTimeout:              15 * time.Second,\n\t})\n\tif err != nil {\n\t\treturn nil, nil, errors.New(\"Creation of new instance failed\")\n\t}\n\treq1, err := inst.NewRequest(\"POST\", \"/api/v1/write\", nil)\n\tif err != nil {\n\t\treturn nil, nil, errors.New(\"Creation of new request failed\")\n\t}\n\treturn inst, req1, nil\n}\n\nfunc makeGAE(t testing.TB) (graph.QuadStore, graph.Options, func()) {\n\tinst, r, err := createInstance()\n\trequire.NoError(t, err)\n\tqs, err := newQuadStore(\"\", nil)\n\tif err != nil {\n\t\tinst.Close()\n\t\tt.Fatal(err)\n\t}\n\tqs, err = qs.(*QuadStore).ForRequest(r)\n\tif err != nil {\n\t\tinst.Close()\n\t\tt.Fatal(err)\n\t}\n\treturn qs, nil, func() {\n\t\tqs.Close()\n\t\tinst.Close()\n\t}\n}\n\nfunc TestGAEAll(t *testing.T) {\n\tgraphtest.TestAll(t, makeGAE, &graphtest.Config{\n\t\tNoPrimitives: true,\n\t\tUnTyped:      true,\n\t})\n}\n\nfunc TestIterators(t *testing.T) {\n\tqs, opts, closer := makeGAE(t)\n\tdefer closer()\n\n\ttestutil.MakeWriter(t, qs, opts, graphtest.MakeQuadSet()...)\n\n\trequire.Equal(t, int64(11), qs.Size(), \"Incorrect number of quads\")\n\n\tvar expected = []quad.Quad{\n\t\tquad.Make(\"C\", \"follows\", \"B\", \"\"),\n\t\tquad.Make(\"C\", \"follows\", \"D\", \"\"),\n\t}\n\n\tit := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw(\"C\")))\n\tgraphtest.ExpectIteratedQuads(t, qs, it, expected, false)\n\n\t// Test contains\n\tit = qs.QuadIterator(quad.Label, qs.ValueOf(quad.Raw(\"status_graph\")))\n\tgqs := qs.(*QuadStore)\n\tkey := gqs.createKeyForQuad(quad.Make(\"G\", \"status\", \"cool\", \"status_graph\"))\n\ttoken := &Token{quadKind, key.StringID()}\n\n\trequire.True(t, it.Contains(token), \"Contains failed\")\n}\n"
  },
  {
    "path": "graph/graphmock/graphmock.go",
    "content": "package graphmock\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar (\n\t_ graph.Ref = IntVal(0)\n\t_ graph.Ref = StringNode(\"\")\n)\n\ntype IntVal int\n\nfunc (v IntVal) Key() interface{} { return v }\n\ntype StringNode string\n\nfunc (s StringNode) Key() interface{} { return s }\n\n// Oldstore is a mocked version of the QuadStore interface, for use in tests.\ntype Oldstore struct {\n\tParse bool\n\tData  []string\n\tIter  iterator.Shape\n}\n\nfunc (qs *Oldstore) valueAt(i int) quad.Value {\n\tif !qs.Parse {\n\t\treturn quad.Raw(qs.Data[i])\n\t}\n\tiv, err := strconv.Atoi(qs.Data[i])\n\tif err == nil {\n\t\treturn quad.Int(iv)\n\t}\n\treturn quad.String(qs.Data[i])\n}\n\nfunc (qs *Oldstore) ValueOf(s quad.Value) (graph.Ref, error) {\n\tif s == nil {\n\t\treturn nil, nil\n\t}\n\tfor i := range qs.Data {\n\t\tif va := qs.valueAt(i); va != nil && s.String() == va.String() {\n\t\t\treturn iterator.Int64Node(i), nil\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc (qs *Oldstore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn nopWriter{}, nil\n}\n\ntype nopWriter struct{}\n\nfunc (nopWriter) WriteQuad(q quad.Quad) error {\n\treturn nil\n}\n\nfunc (nopWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\treturn len(buf), nil\n}\n\nfunc (nopWriter) Close() error {\n\treturn nil\n}\n\nfunc (qs *Oldstore) ApplyDeltas([]graph.Delta, graph.IgnoreOpts) error { return nil }\n\nfunc (qs *Oldstore) Quad(graph.Ref) quad.Quad { return quad.Quad{} }\n\nfunc (qs *Oldstore) QuadIterator(d quad.Direction, i graph.Ref) iterator.Shape {\n\treturn qs.Iter\n}\n\nfunc (qs *Oldstore) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {\n\tst, err := qs.Iter.Stats(ctx)\n\treturn st.Size, err\n}\n\nfunc (qs *Oldstore) NodesAllIterator() iterator.Shape { return &iterator.Null{} }\n\nfunc (qs *Oldstore) QuadsAllIterator() iterator.Shape { return &iterator.Null{} }\n\nfunc (qs *Oldstore) NameOf(v graph.Ref) (quad.Value, error) {\n\tswitch v := v.(type) {\n\tcase iterator.Int64Node:\n\t\ti := int(v)\n\t\tif i < 0 || i >= len(qs.Data) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn qs.valueAt(i), nil\n\tcase StringNode:\n\t\tif qs.Parse {\n\t\t\treturn quad.String(v), nil\n\t\t}\n\t\treturn quad.Raw(string(v)), nil\n\tdefault:\n\t\treturn nil, nil\n\t}\n}\n\nfunc (qs *Oldstore) Size() int64 { return 0 }\n\nfunc (qs *Oldstore) DebugPrint() {}\n\nfunc (qs *Oldstore) OptimizeIterator(it iterator.Shape) (iterator.Shape, bool) {\n\treturn iterator.NewNull(), false\n}\n\nfunc (qs *Oldstore) Close() error { return nil }\n\nfunc (qs *Oldstore) QuadDirection(graph.Ref, quad.Direction) graph.Ref {\n\treturn iterator.Int64Node(0)\n}\n\nfunc (qs *Oldstore) RemoveQuad(t quad.Quad) {}\n\nfunc (qs *Oldstore) Type() string { return \"oldmockstore\" }\n\ntype Store struct {\n\tData []quad.Quad\n}\n\nvar _ graph.QuadStore = &Store{}\n\nfunc (qs *Store) ValueOf(s quad.Value) (graph.Ref, error) {\n\tfor _, q := range qs.Data {\n\t\tif q.Subject == s || q.Object == s {\n\t\t\treturn refs.PreFetched(s), nil\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc (qs *Store) ApplyDeltas([]graph.Delta, graph.IgnoreOpts) error { return nil }\n\nfunc (qs *Store) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn nopWriter{}, nil\n}\n\ntype quadValue struct {\n\tq quad.Quad\n}\n\nfunc (q quadValue) Key() interface{} {\n\treturn q.q.String()\n}\n\nfunc (qs *Store) Quad(v graph.Ref) (quad.Quad, error) {\n\treturn v.(quadValue).q, nil\n}\n\nfunc (qs *Store) NameOf(v graph.Ref) (quad.Value, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\treturn v.(refs.PreFetchedValue).NameOf(), nil\n}\n\nfunc (qs *Store) RemoveQuad(t quad.Quad) {}\n\nfunc (qs *Store) Type() string { return \"mockstore\" }\n\nfunc (qs *Store) QuadDirection(v graph.Ref, d quad.Direction) (graph.Ref, error) {\n\tqv, err := qs.Quad(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn refs.PreFetched(qv.Get(d)), nil\n}\n\nfunc (qs *Store) Close() error { return nil }\n\nfunc (qs *Store) DebugPrint() {}\n\nfunc (qs *Store) QuadIterator(d quad.Direction, i graph.Ref) iterator.Shape {\n\tfixed := iterator.NewFixed()\n\tv := i.(refs.PreFetchedValue).NameOf()\n\tfor _, q := range qs.Data {\n\t\tif q.Get(d) == v {\n\t\t\tfixed.Add(quadValue{q})\n\t\t}\n\t}\n\treturn fixed\n}\n\nfunc (qs *Store) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {\n\tv := val.(refs.PreFetchedValue).NameOf()\n\tsz := refs.Size{Exact: true}\n\tfor _, q := range qs.Data {\n\t\tif q.Get(d) == v {\n\t\t\tsz.Value++\n\t\t}\n\t}\n\treturn sz, nil\n}\n\nfunc (qs *Store) NodesAllIterator() iterator.Shape {\n\tset := make(map[string]bool)\n\tfor _, q := range qs.Data {\n\t\tfor _, d := range quad.Directions {\n\t\t\tn, err := qs.NameOf(refs.PreFetched(q.Get(d)))\n\t\t\tif err != nil {\n\t\t\t\treturn iterator.NewError(err)\n\t\t\t}\n\t\t\tif n != nil {\n\t\t\t\tset[n.String()] = true\n\t\t\t}\n\t\t}\n\t}\n\tfixed := iterator.NewFixed()\n\tfor k := range set {\n\t\tfixed.Add(refs.PreFetched(quad.Raw(k)))\n\t}\n\treturn fixed\n}\n\nfunc (qs *Store) QuadsAllIterator() iterator.Shape {\n\tfixed := iterator.NewFixed()\n\tfor _, q := range qs.Data {\n\t\tfixed.Add(quadValue{q})\n\t}\n\treturn fixed\n}\n\nfunc (qs *Store) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\tset := make(map[string]struct{})\n\tfor _, q := range qs.Data {\n\t\tfor _, d := range quad.Directions {\n\t\t\tn, err := qs.NameOf(refs.PreFetched(q.Get(d)))\n\t\t\tif err != nil {\n\t\t\t\treturn graph.Stats{}, err\n\t\t\t}\n\t\t\tif n != nil {\n\t\t\t\tset[n.String()] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\treturn graph.Stats{\n\t\tNodes: refs.Size{Value: int64(len(set)), Exact: true},\n\t\tQuads: refs.Size{Value: int64(len(qs.Data)), Exact: true},\n\t}, nil\n}\n"
  },
  {
    "path": "graph/graphtest/graphtest.go",
    "content": "package graphtest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query/path/pathtest\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/cayley/writer\"\n)\n\ntype Config struct {\n\tNoPrimitives bool\n\tUnTyped      bool // converts all values to Raw representation\n\tTimeInMs     bool\n\tTimeInMcs    bool\n\tTimeRound    bool\n\tPageSize     int // result page size for pagination (large iterator) tests\n\n\tOptimizesComparison bool\n\n\tAlwaysRunIntegration bool // always run integration tests\n\n\tSkipDeletedFromIterator  bool\n\tSkipSizeCheckAfterDelete bool\n}\n\nvar graphTests = []struct {\n\tname string\n\ttest func(t testing.TB, gen testutil.DatabaseFunc, conf *Config)\n}{\n\t{\"load one quad\", TestLoadOneQuad},\n\t{\"load dup\", TestLoadDup},\n\t{\"load dup single\", TestLoadDupSingle},\n\t{\"load dup raw\", TestLoadDupRaw},\n\t{\"delete quad\", TestDeleteQuad},\n\t{\"sizes\", TestSizes},\n\t{\"iterator\", TestIterator},\n\t{\"hasa\", TestHasA},\n\t{\"set iterator\", TestSetIterator},\n\t{\"deleted from iterator\", TestDeletedFromIterator},\n\t{\"load typed quad\", TestLoadTypedQuads},\n\t{\"add and remove\", TestAddRemove},\n\t{\"node delete\", TestNodeDelete},\n\t{\"iterators and next result order\", TestIteratorsAndNextResultOrderA},\n\t{\"compare typed values\", TestCompareTypedValues},\n\t{\"schema\", TestSchema},\n\t{\"delete reinserted\", TestDeleteReinserted},\n\t{\"delete reinserted dup\", TestDeleteReinsertedDup},\n}\n\nfunc TestAll(t *testing.T, gen testutil.DatabaseFunc, conf *Config) {\n\tif conf == nil {\n\t\tconf = &Config{}\n\t}\n\tfor _, gt := range graphTests {\n\t\tt.Run(gt.name, func(t *testing.T) {\n\t\t\tgt.test(t, gen, conf)\n\t\t})\n\t}\n\tt.Run(\"writers\", func(t *testing.T) {\n\t\tTestWriters(t, gen, conf)\n\t})\n\tt.Run(\"1k\", func(t *testing.T) {\n\t\tt.Run(\"tx\", func(t *testing.T) {\n\t\t\tTest1K(t, gen, conf)\n\t\t})\n\t\tt.Run(\"batch\", func(t *testing.T) {\n\t\t\tTest1KBatch(t, gen, conf)\n\t\t})\n\t})\n\tt.Run(\"paths\", func(t *testing.T) {\n\t\tpathtest.RunTestMorphisms(t, gen)\n\t})\n\tt.Run(\"integration\", func(t *testing.T) {\n\t\tTestIntegration(t, gen, conf.AlwaysRunIntegration)\n\t})\n}\n\nfunc BenchmarkAll(b *testing.B, gen testutil.DatabaseFunc, conf *Config) {\n\tb.Run(\"import\", func(b *testing.B) {\n\t\tBenchmarkImport(b, gen)\n\t})\n\tb.Run(\"integration\", func(b *testing.B) {\n\t\tBenchmarkIntegration(b, gen, conf.AlwaysRunIntegration)\n\t})\n}\n\n// MakeQuadSet makes a simple test graph.\n//\n//\t+---+                        +---+\n//\t| A |-------               ->| F |<--\n//\t+---+       \\------>+---+-/  +---+   \\--+---+\n//\t             ------>|#B#|      |        | E |\n//\t+---+-------/      >+---+      |        +---+\n//\t| C |             /            v\n//\t+---+           -/           +---+\n//\t  ----    +---+/             |#G#|\n//\t      \\-->|#D#|------------->+---+\n//\t          +---+\nfunc MakeQuadSet() []quad.Quad {\n\treturn []quad.Quad{\n\t\tquad.Make(\"A\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"D\", nil),\n\t\tquad.Make(\"D\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"B\", \"follows\", \"F\", nil),\n\t\tquad.Make(\"F\", \"follows\", \"G\", nil),\n\t\tquad.Make(\"D\", \"follows\", \"G\", nil),\n\t\tquad.Make(\"E\", \"follows\", \"F\", nil),\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"D\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"G\", \"status\", \"cool\", \"status_graph\"),\n\t}\n}\n\nfunc IteratedQuads(t testing.TB, qs graph.QuadStore, s iterator.Shape) []quad.Quad {\n\tit := s.Iterate()\n\tdefer it.Close()\n\treturn IteratedQuadsNext(t, qs, it)\n}\n\nfunc IteratedQuadsNext(t testing.TB, qs graph.QuadStore, it iterator.Scanner) []quad.Quad {\n\tctx := context.TODO()\n\tvar res quad.ByQuadString\n\tfor it.Next(ctx) {\n\t\tqsq, err := qs.Quad(it.Result())\n\t\trequire.NoError(t, err)\n\t\tres = append(res, qsq)\n\t}\n\trequire.Nil(t, it.Err())\n\tsort.Sort(res)\n\tif res == nil {\n\t\treturn []quad.Quad(nil) // GopherJS seems to have a bug with this type conversion for a nil value\n\t}\n\treturn res\n}\n\nfunc ExpectIteratedQuads(t testing.TB, qs graph.QuadStore, it iterator.Shape, exp []quad.Quad, sortQuads bool) {\n\tgot := IteratedQuads(t, qs, it)\n\tif sortQuads {\n\t\tsort.Sort(quad.ByQuadString(exp))\n\t\tsort.Sort(quad.ByQuadString(got))\n\t}\n\tif len(exp) == 0 {\n\t\texp = nil // GopherJS seems to have a bug with nil value\n\t}\n\trequire.Equal(t, exp, got)\n}\n\nfunc ExpectIteratedRawStrings(t testing.TB, qs graph.QuadStore, it iterator.Shape, exp []string) {\n\t//sort.Strings(exp)\n\tgot := IteratedStrings(t, qs, it)\n\t//sort.Strings(got)\n\trequire.Equal(t, exp, got)\n}\n\nfunc ExpectIteratedValues(t testing.TB, qs graph.QuadStore, it iterator.Shape, exp []quad.Value, sortVals bool) {\n\t//sort.Strings(exp)\n\tgot := IteratedValues(t, qs, it)\n\t//sort.Strings(got)\n\tif sortVals {\n\t\texp = append([]quad.Value{}, exp...)\n\t\tsort.Sort(quad.ByValueString(exp))\n\t}\n\n\trequire.Equal(t, len(exp), len(got), \"%v\\nvs\\n%v\", exp, got)\n\tfor i := range exp {\n\t\tif eq, ok := exp[i].(quad.Equaler); ok {\n\t\t\trequire.True(t, eq.Equal(got[i]))\n\t\t} else {\n\t\t\trequire.True(t, exp[i] == got[i], \"%v\\nvs\\n%v\\n\\n%v\\nvs\\n%v\", exp[i], got[i], exp, got)\n\t\t}\n\t}\n}\n\nfunc IteratedStrings(t testing.TB, qs graph.QuadStore, s iterator.Shape) []string {\n\tctx := context.TODO()\n\tit := s.Iterate()\n\tdefer it.Close()\n\tvar res []string\n\tfor it.Next(ctx) {\n\t\tqsn, err := qs.NameOf(it.Result())\n\t\trequire.Nil(t, err)\n\t\tres = append(res, quad.ToString(qsn))\n\t}\n\trequire.Nil(t, it.Err())\n\tsort.Strings(res)\n\treturn res\n}\n\nfunc IteratedValues(t testing.TB, qs graph.QuadStore, s iterator.Shape) []quad.Value {\n\tctx := context.TODO()\n\tit := s.Iterate()\n\tvar res []quad.Value\n\tfor it.Next(ctx) {\n\t\titn, err := qs.NameOf(it.Result())\n\t\trequire.Nil(t, err)\n\t\tres = append(res, itn)\n\t}\n\trequire.Nil(t, it.Err())\n\tsort.Sort(quad.ByValueString(res))\n\treturn res\n}\n\nfunc TestLoadOneQuad(t testing.TB, gen testutil.DatabaseFunc, c *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\tq := quad.Make(\n\t\t\"Something\",\n\t\t\"points_to\",\n\t\t\"Something Else\",\n\t\t\"context\",\n\t)\n\n\terr := w.AddQuad(q)\n\trequire.NoError(t, err)\n\tfor _, pq := range []quad.String{\"Something\", \"points_to\", \"Something Else\", \"context\"} {\n\t\ttok, err := qs.ValueOf(pq)\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, tok, \"quad store failed to find value: %q\", pq)\n\t\tval, err := qs.NameOf(tok)\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, val, \"quad store failed to decode value: %q\", pq)\n\t\trequire.Equal(t, pq, val, \"quad store failed to roundtrip value: %q\", pq)\n\t}\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 4, Exact: true},\n\t\tQuads: refs.Size{Value: 1, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), []quad.Quad{q}, false)\n}\n\nfunc testLoadDup(t testing.TB, gen testutil.DatabaseFunc, c *Config, single bool) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\tq := quad.Make(\n\t\t\"Something\",\n\t\t\"points_to\",\n\t\t\"Something Else\",\n\t\t\"context\",\n\t)\n\n\tif single {\n\t\terr := w.AddQuadSet([]quad.Quad{q, q})\n\t\trequire.NoError(t, err)\n\t} else {\n\t\terr := w.AddQuad(q)\n\t\trequire.NoError(t, err)\n\t\terr = w.AddQuad(q)\n\t\trequire.NoError(t, err)\n\t}\n\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 4, Exact: true},\n\t\tQuads: refs.Size{Value: 1, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), []quad.Quad{q}, false)\n}\n\nfunc TestLoadDup(t testing.TB, gen testutil.DatabaseFunc, c *Config) {\n\ttestLoadDup(t, gen, c, false)\n}\n\nfunc TestLoadDupSingle(t testing.TB, gen testutil.DatabaseFunc, c *Config) {\n\ttestLoadDup(t, gen, c, true)\n}\n\nfunc TestLoadDupRaw(t testing.TB, gen testutil.DatabaseFunc, c *Config) {\n\tqs, _ := gen(t)\n\n\tq := quad.Make(\n\t\t\"Something\",\n\t\t\"points_to\",\n\t\t\"Something Else\",\n\t\t\"context\",\n\t)\n\n\terr := qs.ApplyDeltas([]graph.Delta{\n\t\t{Quad: q, Action: graph.Add},\n\t\t{Quad: q, Action: graph.Add},\n\t}, graph.IgnoreOpts{IgnoreDup: true})\n\trequire.NoError(t, err)\n\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 4, Exact: true},\n\t\tQuads: refs.Size{Value: 1, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), []quad.Quad{q}, false)\n}\n\nfunc TestWriters(t *testing.T, gen testutil.DatabaseFunc, c *Config) {\n\tt.Run(\"batch\", func(t *testing.T) {\n\t\tqs, _ := gen(t)\n\n\t\tw, err := qs.NewQuadWriter()\n\t\trequire.NoError(t, err)\n\t\tdefer w.Close()\n\n\t\tquads := MakeQuadSet()\n\t\tq1 := quads[:len(quads)/2]\n\t\tq2 := quads[len(q1):]\n\n\t\tn, err := w.WriteQuads(q1)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, len(q1), n)\n\n\t\tn, err = w.WriteQuads(q2)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, len(q2), n)\n\n\t\terr = w.Close()\n\t\trequire.NoError(t, err)\n\n\t\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), quads, true)\n\t})\n\tfor _, mis := range []bool{false, true} {\n\t\tfor _, dup := range []bool{false, true} {\n\t\t\tname := []byte(\"__\")\n\t\t\tif dup {\n\t\t\t\tname[0] = 'd'\n\t\t\t}\n\t\t\tif mis {\n\t\t\t\tname[1] = 'm'\n\t\t\t}\n\t\t\tt.Run(string(name), func(t *testing.T) {\n\t\t\t\tqs, _ := gen(t)\n\n\t\t\t\tw, err := writer.NewSingle(qs, graph.IgnoreOpts{\n\t\t\t\t\tIgnoreDup: dup, IgnoreMissing: mis,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tquads := func(arr ...quad.Quad) {\n\t\t\t\t\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), arr, false)\n\t\t\t\t}\n\n\t\t\t\tdeltaErr := func(exp, err error) {\n\t\t\t\t\tif exp == graph.ErrQuadNotExist && mis {\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else if exp == graph.ErrQuadExists && dup {\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\te, ok := err.(*graph.DeltaError)\n\t\t\t\t\trequire.True(t, ok, \"expected delta error, got: %T (%v)\", err, err)\n\t\t\t\t\trequire.Equal(t, exp, e.Err)\n\t\t\t\t}\n\n\t\t\t\t// add one quad\n\t\t\t\tq := quad.Make(\"a\", \"b\", \"c\", nil)\n\t\t\t\terr = w.AddQuad(q)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tquads(q)\n\n\t\t\t\t// try to add the same quad again\n\t\t\t\terr = w.AddQuad(q)\n\t\t\t\tdeltaErr(graph.ErrQuadExists, err)\n\t\t\t\tquads(q)\n\n\t\t\t\t// remove quad with non-existent node\n\t\t\t\terr = w.RemoveQuad(quad.Make(\"a\", \"b\", \"not-existent\", nil))\n\t\t\t\tdeltaErr(graph.ErrQuadNotExist, err)\n\n\t\t\t\t// remove non-existent quads\n\t\t\t\terr = w.RemoveQuad(quad.Make(\"a\", \"c\", \"b\", nil))\n\t\t\t\tdeltaErr(graph.ErrQuadNotExist, err)\n\t\t\t\terr = w.RemoveQuad(quad.Make(\"c\", \"b\", \"a\", nil))\n\t\t\t\tdeltaErr(graph.ErrQuadNotExist, err)\n\n\t\t\t\t// make sure store is still in correct state\n\t\t\t\tquads(q)\n\n\t\t\t\t// remove existing quad\n\t\t\t\terr = w.RemoveQuad(q)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tquads()\n\n\t\t\t\t// add the same quad again\n\t\t\t\terr = w.AddQuad(q)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tquads(q)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc Test1K(t *testing.T, gen testutil.DatabaseFunc, c *Config) {\n\tqs, _ := gen(t)\n\n\tpg := c.PageSize\n\tif pg == 0 {\n\t\tpg = 100\n\t}\n\tn := pg*3 + 1\n\n\tw, err := writer.NewSingle(qs, graph.IgnoreOpts{})\n\trequire.NoError(t, err)\n\n\tqw := graph.NewWriter(w)\n\texp := make([]quad.Quad, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tq := quad.Make(i, i, i, nil)\n\t\texp = append(exp, q)\n\t\tqw.WriteQuad(q)\n\t}\n\terr = qw.Flush()\n\trequire.NoError(t, err)\n\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), exp, true)\n}\n\nfunc Test1KBatch(t *testing.T, gen testutil.DatabaseFunc, c *Config) {\n\tqs, _ := gen(t)\n\n\tpg := c.PageSize\n\tif pg == 0 {\n\t\tpg = 100\n\t}\n\tn := pg*3 + 1\n\n\texp := make([]quad.Quad, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tq := quad.Make(i, i, i, nil)\n\t\texp = append(exp, q)\n\t}\n\n\tqw, err := qs.NewQuadWriter()\n\trequire.NoError(t, err)\n\tdefer qw.Close()\n\n\tn, err = qw.WriteQuads(exp)\n\trequire.NoError(t, err)\n\trequire.Equal(t, len(exp), n)\n\n\terr = qw.Close()\n\trequire.NoError(t, err)\n\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), exp, true)\n}\n\ntype ValueSizer interface {\n\tSizeOf(graph.Ref) int64\n}\n\nfunc TestSizes(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\terr := w.AddQuadSet(MakeQuadSet())\n\trequire.NoError(t, err)\n\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 11, Exact: true},\n\t\tQuads: refs.Size{Value: 11, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tif qss, ok := qs.(ValueSizer); ok {\n\t\tsn, err := qs.ValueOf(quad.String(\"B\"))\n\t\trequire.Nil(t, err)\n\t\ts := qss.SizeOf(sn)\n\t\trequire.Equal(t, int64(5), s, \"Unexpected quadstore value size\")\n\t}\n\n\terr = w.RemoveQuad(quad.Make(\n\t\t\"A\",\n\t\t\"follows\",\n\t\t\"B\",\n\t\tnil,\n\t))\n\trequire.NoError(t, err)\n\terr = w.RemoveQuad(quad.Make(\n\t\t\"A\",\n\t\t\"follows\",\n\t\t\"B\",\n\t\tnil,\n\t))\n\trequire.True(t, graph.IsQuadNotExist(err))\n\tif !conf.SkipSizeCheckAfterDelete {\n\t\texp = graph.Stats{\n\t\t\tNodes: refs.Size{Value: 10, Exact: true},\n\t\t\tQuads: refs.Size{Value: 10, Exact: true},\n\t\t}\n\t\tst, err := qs.Stats(context.Background(), true)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, exp, st, \"Unexpected quadstore size after RemoveQuad\")\n\t} else {\n\t\texp = graph.Stats{\n\t\t\tNodes: refs.Size{Value: 10, Exact: true},\n\t\t\tQuads: refs.Size{Value: 11, Exact: true},\n\t\t}\n\t\tst, err := qs.Stats(context.Background(), true)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\t}\n\n\tif qss, ok := qs.(ValueSizer); ok {\n\t\tvn, err := qs.ValueOf(quad.String(\"B\"))\n\t\trequire.NoError(t, err)\n\t\ts := qss.SizeOf(vn)\n\t\trequire.Equal(t, int64(4), s, \"Unexpected quadstore value size\")\n\t}\n}\n\nfunc TestIterator(t testing.TB, gen testutil.DatabaseFunc, _ *Config) {\n\tctx := context.TODO()\n\tqs, opts := gen(t)\n\n\ttestutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\tvar it iterator.Shape\n\n\tit = qs.NodesAllIterator()\n\trequire.NotNil(t, it)\n\n\tst, _ := it.Stats(ctx)\n\tsize := st.Size.Value\n\trequire.True(t, size > 0 && size < 23, \"Unexpected size: %v\", size)\n\n\toptIt, changed := it.Optimize(ctx)\n\trequire.True(t, !changed && optIt == it, \"Optimize unexpectedly changed iterator: %v, %T(%p) vs %T(%p)\", changed, optIt, optIt, it, it)\n\n\texpect := []string{\n\t\t\"A\",\n\t\t\"B\",\n\t\t\"C\",\n\t\t\"D\",\n\t\t\"E\",\n\t\t\"F\",\n\t\t\"G\",\n\t\t\"follows\",\n\t\t\"status\",\n\t\t\"cool\",\n\t\t\"status_graph\",\n\t}\n\tsort.Strings(expect)\n\tfor i := 0; i < 2; i++ {\n\t\tgot := IteratedStrings(t, qs, it)\n\t\tsort.Strings(got)\n\t\trequire.Equal(t, expect, got, \"Unexpected iterated result on repeat %d\", i)\n\t}\n\n\titc := it.Lookup()\n\tdefer itc.Close()\n\tfor _, pq := range expect {\n\t\tqsv, err := qs.ValueOf(quad.Raw(pq))\n\t\trequire.NoError(t, err)\n\t\tok := itc.Contains(ctx, qsv)\n\t\trequire.NoError(t, itc.Err())\n\t\trequire.True(t, ok, \"Failed to find and check %q correctly\", pq)\n\n\t}\n\t// FIXME(kortschak) Why does this fail?\n\t/*\n\t\tfor _, pq := range []string{\"baller\"} {\n\t\t\tif it.Contains(qs.ValueOf(pq)) {\n\t\t\t\tt.Errorf(\"Failed to check %q correctly\", pq)\n\t\t\t}\n\t\t}\n\t*/\n\n\tit = qs.QuadsAllIterator()\n\toptIt, changed = it.Optimize(ctx)\n\trequire.True(t, !changed && optIt == it, \"Optimize unexpectedly changed iterator: %v, %T\", changed, optIt)\n\n\titn := it.Iterate()\n\tdefer itn.Close()\n\trequire.True(t, itn.Next(ctx))\n\n\tq, err := qs.Quad(itn.Result())\n\trequire.NoError(t, err)\n\trequire.Nil(t, itn.Err())\n\trequire.True(t, q.IsValid(), \"Invalid quad returned: %q\", q)\n\tset := MakeQuadSet()\n\tvar ok bool\n\tfor _, e := range set {\n\t\tif e.String() == q.String() {\n\t\t\tok = true\n\t\t\tbreak\n\t\t}\n\t}\n\trequire.True(t, ok, \"Failed to find %q during iteration, got:%q\", q, set)\n}\n\nfunc TestHasA(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\ttestutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\tvar it iterator.Shape = graph.NewHasA(qs,\n\t\tgraph.NewLinksTo(qs, qs.NodesAllIterator(), quad.Predicate),\n\t\tquad.Predicate)\n\n\tit, _ = it.Optimize(context.TODO())\n\n\tvar exp []quad.Value\n\tfor i := 0; i < 8; i++ {\n\t\texp = append(exp, quad.Raw(\"follows\"))\n\t}\n\tfor i := 0; i < 3; i++ {\n\t\texp = append(exp, quad.Raw(\"status\"))\n\t}\n\tExpectIteratedValues(t, qs, it, exp, false)\n}\n\nfunc TestSetIterator(t testing.TB, gen testutil.DatabaseFunc, _ *Config) {\n\tqs, opts := gen(t)\n\n\ttestutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\texpectIteratedQuads := func(it iterator.Shape, exp []quad.Quad) {\n\t\tExpectIteratedQuads(t, qs, it, exp, false)\n\t}\n\n\t// Subject iterator.\n\tqsv, err := qs.ValueOf(quad.String(\"C\"))\n\trequire.NoError(t, err)\n\tit := qs.QuadIterator(quad.Subject, qsv)\n\n\texpectIteratedQuads(it, []quad.Quad{\n\t\tquad.Make(\"C\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"D\", nil),\n\t})\n\n\tand := iterator.NewAnd(\n\t\tqs.QuadsAllIterator(),\n\t\tit,\n\t)\n\n\texpectIteratedQuads(and, []quad.Quad{\n\t\tquad.Make(\"C\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"D\", nil),\n\t})\n\n\t// Object iterator.\n\tqsv, err = qs.ValueOf(quad.String(\"F\"))\n\trequire.NoError(t, err)\n\tit = qs.QuadIterator(quad.Object, qsv)\n\n\texpectIteratedQuads(it, []quad.Quad{\n\t\tquad.Make(\"B\", \"follows\", \"F\", nil),\n\t\tquad.Make(\"E\", \"follows\", \"F\", nil),\n\t})\n\n\tqsv, err = qs.ValueOf(quad.String(\"B\"))\n\trequire.NoError(t, err)\n\tand = iterator.NewAnd(\n\t\tqs.QuadIterator(quad.Subject, qsv),\n\t\tit,\n\t)\n\n\texpectIteratedQuads(and, []quad.Quad{\n\t\tquad.Make(\"B\", \"follows\", \"F\", nil),\n\t})\n\n\t// Predicate iterator.\n\tqsv, err = qs.ValueOf(quad.String(\"status\"))\n\trequire.NoError(t, err)\n\tit = qs.QuadIterator(quad.Predicate, qsv)\n\n\texpectIteratedQuads(it, []quad.Quad{\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"D\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"G\", \"status\", \"cool\", \"status_graph\"),\n\t})\n\n\t// Label iterator.\n\tqsv, err = qs.ValueOf(quad.String(\"status_graph\"))\n\trequire.NoError(t, err)\n\tit = qs.QuadIterator(quad.Label, qsv)\n\n\texpectIteratedQuads(it, []quad.Quad{\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"D\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"G\", \"status\", \"cool\", \"status_graph\"),\n\t})\n\n\t// Order is important\n\tqsv, err = qs.ValueOf(quad.String(\"B\"))\n\trequire.NoError(t, err)\n\tand = iterator.NewAnd(\n\t\tqs.QuadIterator(quad.Subject, qsv),\n\t\tit,\n\t)\n\n\texpectIteratedQuads(and, []quad.Quad{\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t})\n\n\t// Order is important\n\tqsv, err = qs.ValueOf(quad.String(\"B\"))\n\trequire.NoError(t, err)\n\tand = iterator.NewAnd(\n\t\tit,\n\t\tqs.QuadIterator(quad.Subject, qsv),\n\t)\n\n\texpectIteratedQuads(and, []quad.Quad{\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t})\n}\n\nfunc TestDeleteQuad(t testing.TB, gen testutil.DatabaseFunc, _ *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\tvn, err := qs.ValueOf(quad.Raw(\"E\"))\n\trequire.NoError(t, err)\n\trequire.NotNil(t, vn)\n\n\tit := qs.QuadIterator(quad.Subject, vn)\n\tExpectIteratedQuads(t, qs, it, []quad.Quad{\n\t\tquad.Make(\"E\", \"follows\", \"F\", nil),\n\t}, false)\n\n\terr = w.RemoveQuad(quad.Make(\"E\", \"follows\", \"F\", nil))\n\trequire.NoError(t, err)\n\n\tqsv, err := qs.ValueOf(quad.Raw(\"E\"))\n\trequire.NoError(t, err)\n\tit = qs.QuadIterator(quad.Subject, qsv)\n\tExpectIteratedQuads(t, qs, it, nil, false)\n\n\tit = qs.QuadsAllIterator()\n\tExpectIteratedQuads(t, qs, it, []quad.Quad{\n\t\tquad.Make(\"A\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"D\", nil),\n\t\tquad.Make(\"D\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"B\", \"follows\", \"F\", nil),\n\t\tquad.Make(\"F\", \"follows\", \"G\", nil),\n\t\tquad.Make(\"D\", \"follows\", \"G\", nil),\n\t\tquad.Make(\"B\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"D\", \"status\", \"cool\", \"status_graph\"),\n\t\tquad.Make(\"G\", \"status\", \"cool\", \"status_graph\"),\n\t}, true)\n}\n\nfunc TestDeletedFromIterator(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tif conf.SkipDeletedFromIterator {\n\t\tt.SkipNow()\n\t}\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\t// Subject iterator.\n\tqsv, err := qs.ValueOf(quad.Raw(\"E\"))\n\trequire.NoError(t, err)\n\tit := qs.QuadIterator(quad.Subject, qsv)\n\n\tExpectIteratedQuads(t, qs, it, []quad.Quad{\n\t\tquad.Make(\"E\", \"follows\", \"F\", nil),\n\t}, false)\n\n\tw.RemoveQuad(quad.Make(\"E\", \"follows\", \"F\", nil))\n\n\tExpectIteratedQuads(t, qs, it, nil, false)\n}\n\nfunc TestLoadTypedQuads(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\tvalues := []quad.Value{\n\t\tquad.BNode(\"A\"), quad.IRI(\"name\"), quad.String(\"B\"), quad.IRI(\"graph\"),\n\t\tquad.IRI(\"B\"), quad.Raw(\"<type>\"),\n\t\tquad.TypedString{Value: \"10\", Type: \"int\"},\n\t\tquad.LangString{Value: \"value\", Lang: \"en\"},\n\t\tquad.Int(-123456789),\n\t\tquad.Float(-12345e-6),\n\t\tquad.Bool(true),\n\t\tquad.Time(time.Now()),\n\t}\n\n\terr := w.AddQuadSet([]quad.Quad{\n\t\t{values[0], values[1], values[2], values[3]},\n\t\t{values[4], values[5], values[6], nil},\n\t\t{values[4], values[5], values[7], nil},\n\t\t{values[0], values[1], values[8], nil},\n\t\t{values[0], values[1], values[9], nil},\n\t\t{values[0], values[1], values[10], nil},\n\t\t{values[0], values[1], values[11], nil},\n\t})\n\trequire.NoError(t, err)\n\tfor _, pq := range values {\n\t\tqsv, err := qs.ValueOf(pq)\n\t\trequire.NoError(t, err)\n\t\tgot, err := qs.NameOf(qsv)\n\t\trequire.NoError(t, err)\n\t\tif !conf.UnTyped {\n\t\t\tif pt, ok := pq.(quad.Time); ok {\n\t\t\t\tvar trim int64\n\t\t\t\tif conf.TimeInMcs {\n\t\t\t\t\ttrim = 1000\n\t\t\t\t} else if conf.TimeInMs {\n\t\t\t\t\ttrim = 1000000\n\t\t\t\t}\n\t\t\t\tif trim > 0 {\n\t\t\t\t\ttm := time.Time(pt)\n\t\t\t\t\tseconds := tm.Unix()\n\t\t\t\t\tnanos := int64(tm.Sub(time.Unix(seconds, 0)))\n\t\t\t\t\tif conf.TimeRound {\n\t\t\t\t\t\tnanos = (nanos/trim + ((nanos/(trim/10))%10)/5) * trim\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnanos = (nanos / trim) * trim\n\t\t\t\t\t}\n\t\t\t\t\tpq = quad.Time(time.Unix(seconds, nanos).UTC())\n\t\t\t\t}\n\t\t\t}\n\t\t\tif eq, ok := pq.(quad.Equaler); ok {\n\t\t\t\tassert.True(t, eq.Equal(got), \"Failed to roundtrip %q (%T), got %q (%T)\", pq, pq, got, got)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, pq, got, \"Failed to roundtrip %q (%T)\", pq, pq)\n\t\t\t}\n\t\t\t// check if we can get received value again (hash roundtrip)\n\t\t\tgotv, err := qs.ValueOf(got)\n\t\t\trequire.NoError(t, err)\n\t\t\tgot2, err := qs.NameOf(gotv)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, got, got2, \"Failed to use returned value to get it again\")\n\t\t} else {\n\t\t\tassert.Equal(t, quad.StringOf(pq), quad.StringOf(got), \"Failed to roundtrip raw %q (%T)\", pq, pq)\n\t\t}\n\t}\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 12, Exact: true},\n\t\tQuads: refs.Size{Value: 7, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n}\n\n// TestAddRemove tests add and remove\n// TODO(dennwc): add tests to verify that QS behaves in a right way with IgnoreOptions,\n// returns ErrQuadExists, ErrQuadNotExists is doing rollback.\nfunc TestAddRemove(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\tif opts == nil {\n\t\topts = make(graph.Options)\n\t}\n\topts[\"ignore_duplicate\"] = true\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 11, Exact: true},\n\t\tQuads: refs.Size{Value: 11, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tall := qs.NodesAllIterator()\n\texpect := []string{\n\t\t\"A\",\n\t\t\"B\",\n\t\t\"C\",\n\t\t\"D\",\n\t\t\"E\",\n\t\t\"F\",\n\t\t\"G\",\n\t\t\"cool\",\n\t\t\"follows\",\n\t\t\"status\",\n\t\t\"status_graph\",\n\t}\n\tExpectIteratedRawStrings(t, qs, all, expect)\n\n\t// Add more quads, some conflicts\n\terr = w.AddQuadSet([]quad.Quad{\n\t\tquad.Make(\"A\", \"follows\", \"B\", nil), // duplicate\n\t\tquad.Make(\"F\", \"follows\", \"B\", nil),\n\t\tquad.Make(\"C\", \"follows\", \"D\", nil), // duplicate\n\t\tquad.Make(\"X\", \"follows\", \"B\", nil),\n\t})\n\tassert.Nil(t, err, \"AddQuadSet failed\")\n\n\texp = graph.Stats{\n\t\tNodes: refs.Size{Value: 12, Exact: true},\n\t\tQuads: refs.Size{Value: 13, Exact: true},\n\t}\n\tst, err = qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tall = qs.NodesAllIterator()\n\texpect = []string{\n\t\t\"A\",\n\t\t\"B\",\n\t\t\"C\",\n\t\t\"D\",\n\t\t\"E\",\n\t\t\"F\",\n\t\t\"G\",\n\t\t\"X\",\n\t\t\"cool\",\n\t\t\"follows\",\n\t\t\"status\",\n\t\t\"status_graph\",\n\t}\n\tExpectIteratedRawStrings(t, qs, all, expect)\n\n\t// Remove quad\n\ttoRemove := quad.Make(\"X\", \"follows\", \"B\", nil)\n\terr = w.RemoveQuad(toRemove)\n\trequire.Nil(t, err, \"RemoveQuad failed\")\n\terr = w.RemoveQuad(toRemove)\n\trequire.True(t, graph.IsQuadNotExist(err), \"expected not exists error, got: %v\", err)\n\n\texpect = []string{\n\t\t\"A\",\n\t\t\"B\",\n\t\t\"C\",\n\t\t\"D\",\n\t\t\"E\",\n\t\t\"F\",\n\t\t\"G\",\n\t\t\"cool\",\n\t\t\"follows\",\n\t\t\"status\",\n\t\t\"status_graph\",\n\t}\n\tall = qs.NodesAllIterator()\n\tExpectIteratedRawStrings(t, qs, all, expect)\n}\n\nfunc TestIteratorsAndNextResultOrderA(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tctx := context.TODO()\n\tqs, opts := gen(t)\n\n\ttestutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 11, Exact: true},\n\t\tQuads: refs.Size{Value: 11, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tqsv, err := qs.ValueOf(quad.Raw(\"C\"))\n\trequire.NoError(t, err)\n\tfixed := iterator.NewFixed(qsv)\n\n\tqsv, err = qs.ValueOf(quad.Raw(\"follows\"))\n\trequire.NoError(t, err)\n\tfixed2 := iterator.NewFixed(qsv)\n\n\tall := qs.NodesAllIterator()\n\n\tconst allTag = \"all\"\n\tinnerAnd := iterator.NewAnd(\n\t\tgraph.NewLinksTo(qs, fixed2, quad.Predicate),\n\t\tgraph.NewLinksTo(qs, iterator.Tag(all, allTag), quad.Object),\n\t)\n\n\thasa := graph.NewHasA(qs, innerAnd, quad.Subject)\n\touterAnd := iterator.NewAnd(fixed, hasa).Iterate()\n\n\trequire.True(t, outerAnd.Next(ctx), \"Expected one matching subtree\")\n\n\tval := outerAnd.Result()\n\tqsn, err := qs.NameOf(val)\n\trequire.NoError(t, err)\n\trequire.Equal(t, quad.Raw(\"C\"), qsn)\n\n\tvar (\n\t\tgot    []string\n\t\texpect = []string{\"B\", \"D\"}\n\t)\n\tfor {\n\t\tm := make(map[string]graph.Ref, 1)\n\t\touterAnd.TagResults(m)\n\t\tqsn, err = qs.NameOf(m[allTag])\n\t\trequire.NoError(t, err)\n\t\tgot = append(got, quad.ToString(qsn))\n\t\tif !outerAnd.NextPath(ctx) {\n\t\t\tbreak\n\t\t}\n\t}\n\tsort.Strings(got)\n\n\trequire.Equal(t, expect, got)\n\n\trequire.True(t, !outerAnd.Next(ctx), \"More than one possible top level output?\")\n}\n\nconst lt, lte, gt, gte = iterator.CompareLT, iterator.CompareLTE, iterator.CompareGT, iterator.CompareGTE\n\nvar tzero = time.Unix(time.Now().Unix(), 0)\n\nvar casesCompare = []struct {\n\top     iterator.Operator\n\tval    quad.Value\n\texpect []quad.Value\n}{\n\t{lt, quad.BNode(\"b\"), []quad.Value{\n\t\tquad.BNode(\"alice\"),\n\t}},\n\t{lte, quad.BNode(\"bob\"), []quad.Value{\n\t\tquad.BNode(\"alice\"), quad.BNode(\"bob\"),\n\t}},\n\t{lt, quad.String(\"b\"), []quad.Value{\n\t\tquad.String(\"alice\"),\n\t}},\n\t{lte, quad.String(\"bob\"), []quad.Value{\n\t\tquad.String(\"alice\"), quad.String(\"bob\"),\n\t}},\n\t{gte, quad.String(\"b\"), []quad.Value{\n\t\tquad.String(\"bob\"), quad.String(\"charlie\"), quad.String(\"dani\"),\n\t}},\n\t{lt, quad.IRI(\"b\"), []quad.Value{\n\t\tquad.IRI(\"alice\"),\n\t}},\n\t{lte, quad.IRI(\"bob\"), []quad.Value{\n\t\tquad.IRI(\"alice\"), quad.IRI(\"bob\"),\n\t}},\n\t{lte, quad.IRI(\"bob\"), []quad.Value{\n\t\tquad.IRI(\"alice\"), quad.IRI(\"bob\"),\n\t}},\n\t{gte, quad.Int(111), []quad.Value{\n\t\tquad.Int(112), quad.Int(math.MaxInt64 - 1), quad.Int(math.MaxInt64),\n\t}},\n\t{gte, quad.Int(110), []quad.Value{\n\t\tquad.Int(110), quad.Int(112), quad.Int(math.MaxInt64 - 1), quad.Int(math.MaxInt64),\n\t}},\n\t{lt, quad.Int(20), []quad.Value{\n\t\tquad.Int(math.MinInt64 + 1), quad.Int(math.MinInt64),\n\t}},\n\t{lte, quad.Int(20), []quad.Value{\n\t\tquad.Int(math.MinInt64 + 1), quad.Int(math.MinInt64), quad.Int(20),\n\t}},\n\t{lte, quad.Time(tzero.Add(time.Hour)), []quad.Value{\n\t\tquad.Time(tzero), quad.Time(tzero.Add(time.Hour)),\n\t}},\n\t{gt, quad.Time(tzero.Add(time.Hour)), []quad.Value{\n\t\tquad.Time(tzero.Add(time.Hour * 49)), quad.Time(tzero.Add(time.Hour * 24 * 365)),\n\t}},\n\t// precision tests\n\t{gt, quad.Int(math.MaxInt64 - 1), []quad.Value{\n\t\tquad.Int(math.MaxInt64),\n\t}},\n\t{gte, quad.Int(math.MaxInt64 - 1), []quad.Value{\n\t\tquad.Int(math.MaxInt64 - 1), quad.Int(math.MaxInt64),\n\t}},\n\t{lt, quad.Int(math.MinInt64 + 1), []quad.Value{\n\t\tquad.Int(math.MinInt64),\n\t}},\n\t{lte, quad.Int(math.MinInt64 + 1), []quad.Value{\n\t\tquad.Int(math.MinInt64 + 1), quad.Int(math.MinInt64),\n\t}},\n}\n\nfunc TestCompareTypedValues(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tif conf.UnTyped {\n\t\tt.SkipNow()\n\t}\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\tt1 := tzero\n\tt2 := t1.Add(time.Hour)\n\tt3 := t2.Add(time.Hour * 48)\n\tt4 := t1.Add(time.Hour * 24 * 365)\n\n\tquads := []quad.Quad{\n\t\t{quad.BNode(\"alice\"), quad.BNode(\"bob\"), quad.BNode(\"charlie\"), quad.BNode(\"dani\")},\n\t\t{quad.IRI(\"alice\"), quad.IRI(\"bob\"), quad.IRI(\"charlie\"), quad.IRI(\"dani\")},\n\t\t{quad.String(\"alice\"), quad.String(\"bob\"), quad.String(\"charlie\"), quad.String(\"dani\")},\n\t\t{quad.Int(100), quad.Int(112), quad.Int(110), quad.Int(20)},\n\t\t{quad.Time(t1), quad.Time(t2), quad.Time(t3), quad.Time(t4)},\n\t\t// test precision as well\n\t\t{quad.Int(math.MaxInt64), quad.Int(math.MaxInt64 - 1), quad.Int(math.MinInt64 + 1), quad.Int(math.MinInt64)},\n\t}\n\n\terr := w.AddQuadSet(quads)\n\trequire.NoError(t, err)\n\n\tvar vals []quad.Value\n\tfor _, q := range quads {\n\t\tfor _, d := range quad.Directions {\n\t\t\tif v := q.Get(d); v != nil {\n\t\t\t\tvals = append(vals, v)\n\t\t\t}\n\t\t}\n\t}\n\tExpectIteratedValues(t, qs, qs.NodesAllIterator(), vals, true)\n\n\tfor _, c := range casesCompare {\n\t\t//t.Log(c.op, c.val)\n\t\tit := iterator.NewComparison(qs.NodesAllIterator(), c.op, c.val, qs)\n\t\tExpectIteratedValues(t, qs, it, c.expect, true)\n\t}\n\n\tctx := context.TODO()\n\tfor _, c := range casesCompare {\n\t\ts := shape.Compare(shape.AllNodes{}, c.op, c.val)\n\t\tns, ok := shape.Optimize(ctx, s, qs)\n\t\trequire.Equal(t, conf.OptimizesComparison, ok)\n\t\tif conf.OptimizesComparison {\n\t\t\trequire.NotEqual(t, s, ns)\n\t\t} else {\n\t\t\trequire.Equal(t, s, ns)\n\t\t}\n\t\tnit := shape.BuildIterator(ctx, qs, ns)\n\t\tExpectIteratedValues(t, qs, nit, c.expect, true)\n\t}\n}\n\nfunc TestNodeDelete(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\tdel := quad.Raw(\"D\")\n\n\terr := w.RemoveNode(del)\n\trequire.NoError(t, err)\n\n\texp := MakeQuadSet()\n\tfor i := 0; i < len(exp); i++ {\n\t\tfor _, d := range quad.Directions {\n\t\t\tif exp[i].Get(d) == del {\n\t\t\t\texp = append(exp[:i], exp[i+1:]...)\n\t\t\t\ti--\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tExpectIteratedQuads(t, qs, qs.QuadsAllIterator(), exp, true)\n\n\tExpectIteratedValues(t, qs, qs.NodesAllIterator(), []quad.Value{\n\t\tquad.Raw(\"A\"),\n\t\tquad.Raw(\"B\"),\n\t\tquad.Raw(\"C\"),\n\t\tquad.Raw(\"E\"),\n\t\tquad.Raw(\"F\"),\n\t\tquad.Raw(\"G\"),\n\t\tquad.Raw(\"cool\"),\n\t\tquad.Raw(\"follows\"),\n\t\tquad.Raw(\"status\"),\n\t\tquad.Raw(\"status_graph\"),\n\t}, true)\n}\n\nfunc TestSchema(t testing.TB, gen testutil.DatabaseFunc, conf *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\ttype Person struct {\n\t\t_         struct{}   `quad:\"@type > ex:Person\"`\n\t\tID        quad.IRI   `quad:\"@id\" json:\"id\"`\n\t\tName      string     `quad:\"ex:name\" json:\"name\"`\n\t\tSomething []quad.IRI `quad:\"isParentOf < *,optional\" json:\"something\"`\n\t}\n\tp := Person{\n\t\tID:   quad.IRI(\"ex:bob\"),\n\t\tName: \"Bob\",\n\t}\n\n\tsch := schema.NewConfig()\n\n\tqw := graph.NewWriter(w)\n\tid, err := sch.WriteAsQuads(qw, p)\n\trequire.NoError(t, err)\n\terr = qw.Close()\n\trequire.NoError(t, err)\n\trequire.Equal(t, p.ID, id)\n\n\tvar p2 Person\n\terr = sch.LoadTo(nil, qs, &p2, id)\n\trequire.NoError(t, err)\n\trequire.Equal(t, p, p2)\n}\n\nfunc TestDeleteReinserted(t testing.TB, gen testutil.DatabaseFunc, _ *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\terr := w.AddQuadSet([]quad.Quad{\n\t\tquad.Make(\"<bob>\", \"<status>\", \"Feeling happy\", nil),\n\t\tquad.Make(\"<sally>\", \"<follows>\", \"<jim>\", nil),\n\t})\n\trequire.NoError(t, err, \"Add quadset failed\")\n\n\tctx := context.TODO()\n\n\tq := quad.Make(\"<bob>\", \"<follows>\", \"<sally>\", nil)\n\tfor i := 0; i < 2; i++ {\n\t\terr = w.AddQuad(q)\n\t\trequire.NoError(t, err, \"Add quad failed\")\n\t\terr = w.RemoveQuad(q)\n\t\trequire.NoError(t, err, \"Remove quad failed\")\n\t\trefs, err := graph.RefsOf(ctx, qs, []quad.Value{\n\t\t\tq.Subject, q.Predicate, q.Object,\n\t\t})\n\t\trequire.NoError(t, err, \"Get values failed\")\n\t\trequire.Len(t, refs, 3)\n\t\tfor _, r := range refs {\n\t\t\trequire.NotNil(t, r)\n\t\t}\n\t}\n}\n\nfunc TestDeleteReinsertedDup(t testing.TB, gen testutil.DatabaseFunc, _ *Config) {\n\tqs, opts := gen(t)\n\n\tw := testutil.MakeWriter(t, qs, opts, MakeQuadSet()...)\n\n\terr := w.AddQuadSet([]quad.Quad{\n\t\tquad.Make(\"<bob>\", \"<status>\", \"Feeling happy\", nil),\n\t\tquad.Make(\"<sally>\", \"<follows>\", \"<jim>\", nil),\n\t})\n\trequire.NoError(t, err, \"Add quadset failed\")\n\n\tctx := context.TODO()\n\n\tq := quad.Make(\"<bob>\", \"<follows>\", \"<x>\", nil)\n\tfor i := 0; i < 2; i++ {\n\t\terr = w.AddQuad(q)\n\t\trequire.NoError(t, err, \"Add quad failed\")\n\t\t// must be ignored\n\t\terr = w.AddQuad(q)\n\t\trequire.NoError(t, err, \"Add quad failed\")\n\t\terr = w.RemoveQuad(q)\n\t\trequire.NoError(t, err, \"Remove quad failed\")\n\n\t\trefs, err := graph.RefsOf(ctx, qs, []quad.Value{\n\t\t\tq.Subject, q.Predicate,\n\t\t})\n\t\trequire.NoError(t, err, \"Get values failed\")\n\t\trequire.Len(t, refs, 2)\n\t\tfor _, r := range refs {\n\t\t\trequire.NotNil(t, r)\n\t\t}\n\n\t\t// the node should be garbage-collected\n\t\trefs, err = graph.RefsOf(ctx, qs, []quad.Value{\n\t\t\tq.Object,\n\t\t})\n\t\tif err == nil {\n\t\t\t// FIXME(dennwc): the graphlog.SplitDeltas adds an increment even though the quad is duplicated and ignored\n\t\t\tt.Skip(\"value must be garbage-collected\")\n\t\t}\n\t}\n}\n\nfunc irif(format string, args ...interface{}) quad.IRI {\n\treturn quad.IRI(fmt.Sprintf(format, args...))\n}\n\nfunc BenchmarkImport(b *testing.B, gen testutil.DatabaseFunc) {\n\tb.StopTimer()\n\n\tqs, _ := gen(b)\n\n\tw, err := qs.NewQuadWriter()\n\trequire.NoError(b, err)\n\tdefer w.Close()\n\n\tconst (\n\t\tmult     = 10\n\t\tperBatch = 100\n\t)\n\n\tquads := make([]quad.Quad, 0, mult*b.N)\n\tfor i := 0; i < mult*b.N; i++ {\n\t\tquads = append(quads, quad.Quad{\n\t\t\tSubject:   irif(\"n%d\", i/5),\n\t\t\tPredicate: quad.IRI(\"sub\"),\n\t\t\tObject:    irif(\"n%d\", i/2+i%2),\n\t\t})\n\t}\n\n\tb.ResetTimer()\n\tb.StartTimer()\n\tfor len(quads) > 0 {\n\t\tbatch := quads\n\t\tif len(batch) > perBatch {\n\t\t\tbatch = batch[:perBatch]\n\t\t}\n\t\tn, err := w.WriteQuads(batch)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t} else if n != len(batch) {\n\t\t\tb.Fatal(n)\n\t\t}\n\t\tquads = quads[len(batch):]\n\t}\n\terr = w.Close()\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tb.StopTimer()\n}\n"
  },
  {
    "path": "graph/graphtest/integration.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graphtest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/internal\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/gizmo\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n)\n\nconst (\n\tformat  = \"nquads\"\n\ttimeout = 300 * time.Second\n)\n\nconst (\n\tnSpeed = \"Speed\"\n\tnLakeH = \"The Lake House\"\n\n\tSandraB = \"Sandra Bullock\"\n\tKeanuR  = \"Keanu Reeves\"\n)\n\nfunc checkIntegration(t testing.TB, force bool) {\n\tif testing.Short() {\n\t\tt.SkipNow()\n\t}\n\tif !force && os.Getenv(\"RUN_INTEGRATION\") != \"true\" {\n\t\tt.Skip(\"skipping integration tests; set RUN_INTEGRATION=true to run them\")\n\t}\n}\n\nfunc TestIntegration(t *testing.T, gen testutil.DatabaseFunc, force bool) {\n\tcheckIntegration(t, force)\n\tqs := prepare(t, gen)\n\n\tcheckQueries(t, qs, timeout)\n}\n\nfunc BenchmarkIntegration(t *testing.B, gen testutil.DatabaseFunc, force bool) {\n\tcheckIntegration(t, force)\n\tbenchmarkQueries(t, gen)\n}\n\nfunc costarTag(id, c1, c1m, c2, c2m string) map[string]string {\n\treturn map[string]string{\n\t\t\"id\":            id,\n\t\t\"costar1_actor\": c1,\n\t\t\"costar1_movie\": c1m,\n\t\t\"costar2_actor\": c2,\n\t\t\"costar2_movie\": c2m,\n\t}\n}\n\nvar queries = []struct {\n\tmessage string\n\tlong    bool\n\tquery   string\n\ttag     string\n\t// for testing\n\tskip   bool\n\texpect []interface{}\n}{\n\t// Easy one to get us started. How quick is the most straightforward retrieval?\n\t{\n\t\tmessage: \"name predicate\",\n\t\tquery: `\n\t\tg.V(\"Humphrey Bogart\").in(\"<name>\").all()\n\t\t`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": \"</en/humphrey_bogart>\"},\n\t\t},\n\t},\n\n\t// Grunty queries.\n\t// 2014-07-12: This one seems to return in ~20ms in memory;\n\t// that's going to be measurably slower for every other backend.\n\t{\n\t\tmessage: \"two large sets with no intersection\",\n\t\tquery: `\n\t\tfunction getId(x) { return g.V(x).in(\"<name>\") }\n\t\tvar actor_to_film = g.M().in(\"</film/performance/actor>\").in(\"</film/film/starring>\")\n\n\t\tgetId(\"Oliver Hardy\").follow(actor_to_film).out(\"<name>\").intersect(\n\t\t\tgetId(\"Mel Blanc\").follow(actor_to_film).out(\"<name>\")).all()\n\t\t\t`,\n\t\texpect: nil,\n\t},\n\n\t// 2014-07-12: This one takes about 4 whole seconds in memory. This is a behemoth.\n\t{\n\t\tmessage: \"three huge sets with small intersection\",\n\t\tlong:    true,\n\t\tquery: `\n\t\t\tfunction getId(x) { return g.V(x).in(\"<name>\") }\n\t\t\tvar actor_to_film = g.M().in(\"</film/performance/actor>\").in(\"</film/film/starring>\")\n\n\t\t\tvar a = getId(\"Oliver Hardy\").follow(actor_to_film).followR(actor_to_film)\n\t\t\tvar b = getId(\"Mel Blanc\").follow(actor_to_film).followR(actor_to_film)\n\t\t\tvar c = getId(\"Billy Gilbert\").follow(actor_to_film).followR(actor_to_film)\n\n\t\t\tseen = {}\n\n\t\t\ta.intersect(b).intersect(c).forEach(function (d) {\n\t\t\t\tif (!(d.id in seen)) {\n\t\t\t\t\tseen[d.id] = true;\n\t\t\t\t\tg.emit(d)\n\t\t\t\t}\n\t\t\t})\n\t\t\t`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": \"</en/sterling_holloway>\"},\n\t\t\tmap[string]string{\"id\": \"</en/billy_gilbert>\"},\n\t\t},\n\t},\n\n\t// This is more of an optimization problem that will get better over time. This takes a lot\n\t// of wrong turns on the walk down to what is ultimately the name, but top AND has it easy\n\t// as it has a fixed ID. Exercises Contains().\n\t{\n\t\tmessage: \"the helpless checker\",\n\t\tlong:    true,\n\t\tquery: `\n\t\t\tg.V().as(\"person\").in(\"<name>\").in().in().out(\"<name>\").is(\"Casablanca\").all()\n\t\t\t`,\n\t\ttag: \"person\",\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Ingrid Bergman\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Madeleine LeBeau\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Joy Page\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Claude Rains\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"S.Z. Sakall\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Helmut Dantine\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Conrad Veidt\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Paul Henreid\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Peter Lorre\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Sydney Greenstreet\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Leonid Kinskey\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Lou Marcelle\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Dooley Wilson\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"John Qualen\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Humphrey Bogart\"},\n\t\t},\n\t},\n\n\t// Exercises Not().Contains(), as above.\n\t{\n\t\tmessage: \"the helpless checker, negated (films without Ingrid Bergman)\",\n\t\tlong:    true,\n\t\tquery: `\n\t\t\tg.V().as(\"person\").in(\"<name>\").in().in().out(\"<name>\").except(g.V(\"Ingrid Bergman\").in(\"<name>\").in().in().out(\"<name>\")).is(\"Casablanca\").all()\n\t\t\t`,\n\t\ttag:    \"person\",\n\t\texpect: nil,\n\t},\n\t{\n\t\tmessage: \"the helpless checker, negated (without actors Ingrid Bergman)\",\n\t\tlong:    true,\n\t\tquery: `\n\t\t\tg.V().as(\"person\").in(\"<name>\").except(g.V(\"Ingrid Bergman\").in(\"<name>\")).in().in().out(\"<name>\").is(\"Casablanca\").all()\n\t\t\t`,\n\t\ttag: \"person\",\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Madeleine LeBeau\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Joy Page\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Claude Rains\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"S.Z. Sakall\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Helmut Dantine\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Conrad Veidt\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Paul Henreid\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Peter Lorre\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Sydney Greenstreet\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Leonid Kinskey\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Lou Marcelle\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Dooley Wilson\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"John Qualen\"},\n\t\t\tmap[string]string{\"id\": \"Casablanca\", \"person\": \"Humphrey Bogart\"},\n\t\t},\n\t},\n\n\t//Q: Who starred in both \"The Net\" and \"Speed\" ?\n\t//A: \"Sandra Bullock\"\n\t{\n\t\tmessage: \"Net and Speed\",\n\t\tquery: common + `m1_actors.intersect(m2_actors).out(\"<name>\").all()\n`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": SandraB, \"movie1\": \"The Net\", \"movie2\": nSpeed},\n\t\t},\n\t},\n\n\t//Q: Did \"Keanu Reeves\" star in \"The Net\" ?\n\t//A: No\n\t{\n\t\tmessage: \"Keanu in The Net\",\n\t\tquery: common + `actor2.intersect(m1_actors).out(\"<name>\").all()\n`,\n\t\texpect: nil,\n\t},\n\n\t//Q: Did \"Keanu Reeves\" star in \"Speed\" ?\n\t//A: Yes\n\t{\n\t\tmessage: \"Keanu in Speed\",\n\t\tquery: common + `actor2.intersect(m2_actors).out(\"<name>\").all()\n`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": KeanuR, \"movie2\": nSpeed},\n\t\t},\n\t},\n\n\t//Q: Has \"Keanu Reeves\" co-starred with anyone who starred in \"The Net\" ?\n\t//A: \"Keanu Reeves\" was in \"Speed\" and \"The Lake House\" with \"Sandra Bullock\",\n\t//   who was in \"The Net\"\n\t{\n\t\tmessage: \"Keanu with other in The Net\",\n\t\tlong:    true,\n\t\tquery: common + `actor2.follow(coStars1).intersect(m1_actors).out(\"<name>\").all()\n`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"id\": SandraB, \"movie1\": \"The Net\", \"costar1_movie\": nSpeed},\n\t\t\tmap[string]string{\"movie1\": \"The Net\", \"costar1_movie\": nLakeH, \"id\": SandraB},\n\t\t},\n\t},\n\n\t//Q: Do \"Keanu Reeves\" and \"Sandra Bullock\" have any commons co-stars?\n\t//A: Yes, many. For example: SB starred with \"Steve Martin\" in \"The Prince\n\t//    of Egypt\", and KR starred with Steven Martin in \"Parenthood\".\n\t{\n\t\tmessage: \"Keanu and Bullock with other\",\n\t\tlong:    true,\n\t\tquery: common + `actor1.save(\"<name>\",\"costar1_actor\").follow(coStars1).intersect(actor2.save(\"<name>\",\"costar2_actor\").follow(coStars2)).out(\"<name>\").all()\n`,\n\t\texpect: []interface{}{\n\t\t\tcostarTag(SandraB, SandraB, \"The Proposal\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"The Proposal\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Mary Steenburgen\", SandraB, \"The Proposal\", KeanuR, \"Parenthood\"),\n\t\t\tcostarTag(\"Craig T. Nelson\", SandraB, \"The Proposal\", KeanuR, \"The Devil's Advocate\"),\n\t\t\tcostarTag(SandraB, SandraB, \"Crash\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Crash\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Gun Shy\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Gun Shy\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Demolition Man\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Demolition Man\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Benjamin Bratt\", SandraB, \"Demolition Man\", KeanuR, \"Thumbsucker\"),\n\t\t\tcostarTag(SandraB, SandraB, \"Divine Secrets of the Ya-Ya Sisterhood\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Divine Secrets of the Ya-Ya Sisterhood\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Shirley Knight\", SandraB, \"Divine Secrets of the Ya-Ya Sisterhood\", KeanuR, \"The Private Lives of Pippa Lee\"),\n\t\t\tcostarTag(SandraB, SandraB, \"A Time to Kill\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"A Time to Kill\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Forces of Nature\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Forces of Nature\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Hope Floats\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Hope Floats\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Infamous\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Infamous\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Jeff Daniels\", SandraB, \"Infamous\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Love Potion No. 9\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Love Potion No. 9\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Miss Congeniality\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Miss Congeniality\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Benjamin Bratt\", SandraB, \"Miss Congeniality\", KeanuR, \"Thumbsucker\"),\n\t\t\tcostarTag(SandraB, SandraB, \"Miss Congeniality 2: Armed and Fabulous\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Miss Congeniality 2: Armed and Fabulous\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Murder by Numbers\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Murder by Numbers\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Practical Magic\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Practical Magic\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Dianne Wiest\", SandraB, \"Practical Magic\", KeanuR, \"Parenthood\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Flying\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Animatrix\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Tune in Tomorrow\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Last Time I Committed Suicide\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Constantine\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Permanent Record\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Dangerous Liaisons\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Private Lives of Pippa Lee\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"A Scanner Darkly\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"A Walk in the Clouds\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Hardball\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Life Under Water\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Much Ado About Nothing\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"My Own Private Idaho\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Parenthood\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Point Break\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Providence\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"River's Edge\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Something's Gotta Give\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Sweet November\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, nLakeH),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Matrix Reloaded\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Matrix Revisited\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Prince of Pennsylvania\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Replacements\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Even Cowgirls Get the Blues\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Youngblood\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Bill & Ted's Bogus Journey\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Bill & Ted's Excellent Adventure\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Johnny Mnemonic\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Devil's Advocate\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Thumbsucker\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"I Love You to Death\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Bram Stoker's Dracula\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Gift\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Little Buddha\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Night Watchman\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Chain Reaction\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"Babes in Toyland\"),\n\t\t\tcostarTag(KeanuR, SandraB, nSpeed, KeanuR, \"The Day the Earth Stood Still\"),\n\t\t\tcostarTag(SandraB, SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, nSpeed, KeanuR, nLakeH),\n\t\t\tcostarTag(\"Dennis Hopper\", SandraB, nSpeed, KeanuR, \"River's Edge\"),\n\t\t\tcostarTag(\"Dennis Hopper\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Jeff Daniels\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Joe Morton\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Alan Ruck\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Glenn Plummer\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Carlos Carrasco\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Beth Grant\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Richard Lineback\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Hawthorne James\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Jordan Lund\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(\"Thomas Rosales, Jr.\", SandraB, nSpeed, KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Speed 2: Cruise Control\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Speed 2: Cruise Control\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Glenn Plummer\", SandraB, \"Speed 2: Cruise Control\", KeanuR, nSpeed),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Flying\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Animatrix\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Tune in Tomorrow\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Last Time I Committed Suicide\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Constantine\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Permanent Record\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Dangerous Liaisons\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Private Lives of Pippa Lee\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"A Scanner Darkly\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"A Walk in the Clouds\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Hardball\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Life Under Water\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Much Ado About Nothing\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"My Own Private Idaho\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Parenthood\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Point Break\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Providence\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"River's Edge\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Something's Gotta Give\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, nSpeed),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Sweet November\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Matrix Reloaded\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Matrix Revisited\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Prince of Pennsylvania\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Replacements\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Even Cowgirls Get the Blues\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Youngblood\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Bill & Ted's Bogus Journey\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Bill & Ted's Excellent Adventure\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Johnny Mnemonic\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Devil's Advocate\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Thumbsucker\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"I Love You to Death\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Bram Stoker's Dracula\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Gift\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Little Buddha\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Night Watchman\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Chain Reaction\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"Babes in Toyland\"),\n\t\t\tcostarTag(KeanuR, SandraB, nLakeH, KeanuR, \"The Day the Earth Stood Still\"),\n\t\t\tcostarTag(SandraB, SandraB, nLakeH, KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(\"Christopher Plummer\", SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(\"Dylan Walsh\", SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(\"Shohreh Aghdashloo\", SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(\"Lynn Collins\", SandraB, nLakeH, KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"The Net\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"The Net\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Michelle Pfeiffer\", SandraB, \"The Prince of Egypt\", KeanuR, \"Dangerous Liaisons\"),\n\t\t\tcostarTag(SandraB, SandraB, \"The Prince of Egypt\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"The Prince of Egypt\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Steve Martin\", SandraB, \"The Prince of Egypt\", KeanuR, \"Parenthood\"),\n\t\t\tcostarTag(SandraB, SandraB, \"Two Weeks Notice\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Two Weeks Notice\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"While You Were Sleeping\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"While You Were Sleeping\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Jack Warden\", SandraB, \"While You Were Sleeping\", KeanuR, \"The Replacements\"),\n\t\t\tcostarTag(SandraB, SandraB, \"28 Days\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"28 Days\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Premonition\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Premonition\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"Peter Stormare\", SandraB, \"Premonition\", KeanuR, \"Constantine\"),\n\t\t\tcostarTag(SandraB, SandraB, \"Wrestling Ernest Hemingway\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Wrestling Ernest Hemingway\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"Fire on the Amazon\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"Fire on the Amazon\", KeanuR, nLakeH),\n\t\t\tcostarTag(\"River Phoenix\", SandraB, \"The Thing Called Love\", KeanuR, \"My Own Private Idaho\"),\n\t\t\tcostarTag(\"River Phoenix\", SandraB, \"The Thing Called Love\", KeanuR, \"I Love You to Death\"),\n\t\t\tcostarTag(SandraB, SandraB, \"The Thing Called Love\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"The Thing Called Love\", KeanuR, nLakeH),\n\t\t\tcostarTag(SandraB, SandraB, \"In Love and War\", KeanuR, nSpeed),\n\t\t\tcostarTag(SandraB, SandraB, \"In Love and War\", KeanuR, nLakeH),\n\t\t},\n\t},\n\t{\n\t\tmessage: \"Save a number of predicates around a set of nodes\",\n\t\tquery: `\n\t\tg.V(\"_:9037\", \"_:49278\", \"_:44112\", \"_:44709\", \"_:43382\").save(\"</film/performance/character>\", \"char\").save(\"</film/performance/actor>\", \"act\").saveR(\"</film/film/starring>\", \"film\").all()\n\t\t`,\n\t\texpect: []interface{}{\n\t\t\tmap[string]string{\"act\": \"</en/humphrey_bogart>\", \"char\": \"Rick Blaine\", \"film\": \"</en/casablanca_1942>\", \"id\": \"_:9037\"},\n\t\t\tmap[string]string{\"act\": \"</en/humphrey_bogart>\", \"char\": \"Sam Spade\", \"film\": \"</en/the_maltese_falcon_1941>\", \"id\": \"_:49278\"},\n\t\t\tmap[string]string{\"act\": \"</en/humphrey_bogart>\", \"char\": \"Philip Marlowe\", \"film\": \"</en/the_big_sleep_1946>\", \"id\": \"_:44112\"},\n\t\t\tmap[string]string{\"act\": \"</en/humphrey_bogart>\", \"char\": \"Captain Queeg\", \"film\": \"</en/the_caine_mutiny_1954>\", \"id\": \"_:44709\"},\n\t\t\tmap[string]string{\"act\": \"</en/humphrey_bogart>\", \"char\": \"Charlie Allnut\", \"film\": \"</en/the_african_queen>\", \"id\": \"_:43382\"},\n\t\t},\n\t},\n}\n\nconst common = `\nvar movie1 = g.V().has(\"<name>\", \"The Net\")\nvar movie2 = g.V().has(\"<name>\", \"Speed\")\nvar actor1 = g.V().has(\"<name>\", \"Sandra Bullock\")\nvar actor2 = g.V().has(\"<name>\", \"Keanu Reeves\")\n\n// (film) -> starring -> (actor)\nvar filmToActor = g.Morphism().out(\"</film/film/starring>\").out(\"</film/performance/actor>\")\n\n// (actor) -> starring -> [film -> starring -> (actor)]\nvar coStars1 = g.Morphism().in(\"</film/performance/actor>\").in(\"</film/film/starring>\").save(\"<name>\",\"costar1_movie\").follow(filmToActor)\nvar coStars2 = g.Morphism().in(\"</film/performance/actor>\").in(\"</film/film/starring>\").save(\"<name>\",\"costar2_movie\").follow(filmToActor)\n\n// Stars for the movies \"The Net\" and \"Speed\"\nvar m1_actors = movie1.save(\"<name>\",\"movie1\").follow(filmToActor)\nvar m2_actors = movie2.save(\"<name>\",\"movie2\").follow(filmToActor)\n`\n\nfunc prepare(t testing.TB, gen testutil.DatabaseFunc) graph.QuadStore {\n\tqs, _ := gen(t)\n\n\tconst needsLoad = true // TODO: support local setup\n\tif needsLoad {\n\t\tqw, err := qs.NewQuadWriter()\n\t\tif err != nil {\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\tstart := time.Now()\n\t\tfor _, p := range []string{\"./\", \"../\"} {\n\t\t\terr = internal.Load(qw, 0, filepath.Join(p, \"../../data/30kmoviedata.nq.gz\"), format)\n\t\t\tif err == nil || !os.IsNotExist(err) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\tqw.Close()\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\terr = qw.Close()\n\t\tif err != nil {\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tt.Logf(\"loaded data in %v\", time.Since(start))\n\t}\n\treturn qs\n}\n\nfunc checkQueries(t *testing.T, qs graph.QuadStore, timeout time.Duration) {\n\tif qs == nil {\n\t\tt.Fatal(\"not initialized\")\n\t}\n\tfor _, test := range queries {\n\t\tt.Run(test.message, func(t *testing.T) {\n\t\t\tif testing.Short() && test.long {\n\t\t\t\tt.SkipNow()\n\t\t\t}\n\t\t\tif test.skip {\n\t\t\t\tt.SkipNow()\n\t\t\t}\n\t\t\tstart := time.Now()\n\t\t\tses := gizmo.NewSession(qs)\n\t\t\tctx := context.Background()\n\t\t\tif timeout > 0 {\n\t\t\t\tvar cancel func()\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, timeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\t\t\tit, err := ses.Execute(ctx, test.query, query.Options{\n\t\t\t\tCollation: query.JSON,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer it.Close()\n\t\t\tvar got []interface{}\n\t\t\tfor it.Next(ctx) {\n\t\t\t\tgot = append(got, it.Result())\n\t\t\t}\n\t\t\tt.Logf(\"%12v %v\", time.Since(start), test.message)\n\n\t\t\tif len(got) != len(test.expect) {\n\t\t\t\tt.Errorf(\"Unexpected number of results, got:%d expect:%d on %s.\", len(got), len(test.expect), test.message)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif unsortedEqual(got, test.expect) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.Errorf(\"Unexpected results for %s:\\n\", test.message)\n\t\t\tfor i := range got {\n\t\t\t\tt.Errorf(\"\\n\\tgot:%#v\\n\\texpect:%#v\\n\", got[i], test.expect[i])\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc unsortedEqual(got, expect []interface{}) bool {\n\tgotList := convertToStringList(got)\n\texpectList := convertToStringList(expect)\n\treturn reflect.DeepEqual(gotList, expectList)\n}\n\nfunc convertToStringList(in []interface{}) []string {\n\tvar out []string\n\tfor _, x := range in {\n\t\tif xc, ok := x.(map[string]string); ok {\n\t\t\tfor k, v := range xc {\n\t\t\t\tout = append(out, fmt.Sprint(k, \":\", v))\n\t\t\t}\n\t\t} else {\n\t\t\tfor k, v := range x.(map[string]interface{}) {\n\t\t\t\tout = append(out, fmt.Sprint(k, \":\", v))\n\t\t\t}\n\t\t}\n\t}\n\tsort.Strings(out)\n\treturn out\n}\n\nfunc benchmarkQueries(b *testing.B, gen testutil.DatabaseFunc) {\n\tqs := prepare(b, gen)\n\n\tfor _, bench := range queries {\n\t\tb.Run(bench.message, func(b *testing.B) {\n\t\t\tif testing.Short() && bench.long {\n\t\t\t\tb.Skip()\n\t\t\t}\n\t\t\tb.StopTimer()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tfunc() {\n\t\t\t\t\tctx := context.Background()\n\t\t\t\t\tif timeout > 0 {\n\t\t\t\t\t\tvar cancel func()\n\t\t\t\t\t\tctx, cancel = context.WithTimeout(ctx, timeout)\n\t\t\t\t\t\tdefer cancel()\n\t\t\t\t\t}\n\t\t\t\t\tses := gizmo.NewSession(qs)\n\t\t\t\t\tb.StartTimer()\n\t\t\t\t\tit, err := ses.Execute(ctx, bench.query, query.Options{\n\t\t\t\t\t\tCollation: query.Raw,\n\t\t\t\t\t})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer it.Close()\n\t\t\t\t\tn := 0\n\t\t\t\t\tfor it.Next(ctx) {\n\t\t\t\t\t\tn++\n\t\t\t\t\t}\n\t\t\t\t\tif err = it.Err(); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tb.StopTimer()\n\t\t\t\t\tif n != len(bench.expect) {\n\t\t\t\t\t\tb.Fatalf(\"unexpected number of results: %d vs %d\", n, len(bench.expect))\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "graph/graphtest/testutil/testutil.go",
    "content": "package testutil\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/nquads\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/writer\"\n)\n\ntype DatabaseFunc func(t testing.TB) (graph.QuadStore, graph.Options)\n\nfunc LoadGraph(t testing.TB, path string) []quad.Quad {\n\tvar (\n\t\tf   *os.File\n\t\terr error\n\t)\n\tconst levels = 5\n\tfor i := 0; i < levels; i++ {\n\t\tf, err = os.Open(path)\n\t\tif i+1 < levels && os.IsNotExist(err) {\n\t\t\tpath = filepath.Join(\"../\", path)\n\t\t} else if err != nil {\n\t\t\tt.Fatalf(\"Failed to open %q: %v\", path, err)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tdefer f.Close()\n\tdec := nquads.NewReader(f, false)\n\tquads, err := quad.ReadAll(dec)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to Unmarshal: %v\", err)\n\t}\n\treturn quads\n}\n\nfunc MakeWriter(t testing.TB, qs graph.QuadStore, opts graph.Options, data ...quad.Quad) graph.QuadWriter {\n\tw, err := writer.NewSingleReplication(qs, opts)\n\trequire.NoError(t, err)\n\tif len(data) > 0 {\n\t\terr = w.AddQuadSet(data)\n\t\trequire.NoError(t, err)\n\t}\n\treturn w\n}\n"
  },
  {
    "path": "graph/hasa.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\n// Defines one of the base iterators, the HasA iterator. The HasA takes a\n// subiterator of links, and acts as an iterator of nodes in the given\n// direction. The name comes from the idea that a \"link HasA subject\" or a \"link\n// HasA predicate\".\n//\n// HasA is weird in that it may return the same value twice if on the Next()\n// path. That's okay -- in reality, it can be viewed as returning the value for\n// a new quad, but to make logic much simpler, here we have the HasA.\n//\n// Likewise, it's important to think about Contains()ing a HasA. When given a\n// value to check, it means \"Check all predicates that have this value for your\n// direction against the subiterator.\" This would imply that there's more than\n// one possibility for the same Contains()ed value. While we could return the\n// number of options, it's simpler to return one, and then call NextPath()\n// enough times to enumerate the options. (In fact, one could argue that the\n// raison d'etre for NextPath() is this iterator).\n//\n// Alternatively, can be seen as the dual of the LinksTo iterator.\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// A HasA consists of a reference back to the graph.QuadStore that it references,\n// a primary subiterator, a direction in which the quads for that subiterator point,\n// and a temporary holder for the iterator generated on Contains().\ntype HasA struct {\n\tqs      QuadIndexer\n\tprimary iterator.Shape\n\tdir     quad.Direction\n}\n\n// NewHasA construct a new HasA iterator, given the quad subiterator, and the quad\n// direction for which it stands.\nfunc NewHasA(qs QuadIndexer, subIt iterator.Shape, d quad.Direction) *HasA {\n\treturn &HasA{\n\t\tqs:      qs,\n\t\tprimary: subIt,\n\t\tdir:     d,\n\t}\n}\n\nfunc (it *HasA) Iterate() iterator.Scanner {\n\treturn newHasANext(it.qs, it.primary.Iterate(), it.dir)\n}\n\nfunc (it *HasA) Lookup() iterator.Index {\n\treturn newHasAContains(it.qs, it.primary.Lookup(), it.dir)\n}\n\n// SubIterators returns our sole subiterator.\nfunc (it *HasA) SubIterators() []iterator.Shape {\n\treturn []iterator.Shape{it.primary}\n}\n\n// Direction accessor.\nfunc (it *HasA) Direction() quad.Direction { return it.dir }\n\n// Optimize pass the Optimize() call along to the subiterator. If it becomes Null,\n// then the HasA becomes Null (there are no quads that have any directions).\nfunc (it *HasA) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\tnewPrimary, changed := it.primary.Optimize(ctx)\n\tif changed {\n\t\tit.primary = newPrimary\n\t\tif iterator.IsNull(it.primary) {\n\t\t\treturn it.primary, true\n\t\t}\n\t}\n\treturn it, false\n}\n\nfunc (it *HasA) String() string {\n\treturn fmt.Sprintf(\"HasA(%v)\", it.dir)\n}\n\n// Stats returns the statistics on the HasA iterator. This is curious. Next\n// cost is easy, it's an extra call or so on top of the subiterator Next cost.\n// ContainsCost involves going to the graph.QuadStore, iterating out values, and hoping\n// one sticks -- potentially expensive, depending on fanout. Size, however, is\n// potentially smaller. we know at worst it's the size of the subiterator, but\n// if there are many repeated values, it could be much smaller in totality.\nfunc (it *HasA) Stats(ctx context.Context) (iterator.Costs, error) {\n\tsubitStats, err := it.primary.Stats(ctx)\n\t// TODO(barakmich): These should really come from the quadstore itself\n\t// and be optimized.\n\tfaninFactor := int64(1)\n\tfanoutFactor := int64(30)\n\tnextConstant := int64(2)\n\tquadConstant := int64(1)\n\treturn iterator.Costs{\n\t\tNextCost:     quadConstant + subitStats.NextCost,\n\t\tContainsCost: (fanoutFactor * nextConstant) * subitStats.ContainsCost,\n\t\tSize: refs.Size{\n\t\t\tValue: faninFactor * subitStats.Size.Value,\n\t\t\tExact: false,\n\t\t},\n\t}, err\n}\n\n// A HasA consists of a reference back to the graph.QuadStore that it references,\n// a primary subiterator, a direction in which the quads for that subiterator point,\n// and a temporary holder for the iterator generated on Contains().\ntype hasANext struct {\n\tqs      QuadIndexer\n\tprimary iterator.Scanner\n\tdir     quad.Direction\n\tresult  refs.Ref\n\terr     error\n}\n\n// Construct a new HasA iterator, given the quad subiterator, and the quad\n// direction for which it stands.\nfunc newHasANext(qs QuadIndexer, subIt iterator.Scanner, d quad.Direction) *hasANext {\n\treturn &hasANext{\n\t\tqs:      qs,\n\t\tprimary: subIt,\n\t\tdir:     d,\n\t}\n}\n\n// Direction accessor.\nfunc (it *hasANext) Direction() quad.Direction { return it.dir }\n\n// Pass the TagResults down the chain.\nfunc (it *hasANext) TagResults(dst map[string]refs.Ref) {\n\tit.primary.TagResults(dst)\n}\n\nfunc (it *hasANext) String() string {\n\treturn fmt.Sprintf(\"HasANext(%v)\", it.dir)\n}\n\n// Get the next result that matches this branch.\nfunc (it *hasANext) NextPath(ctx context.Context) bool {\n\treturn it.primary.NextPath(ctx)\n}\n\n// Next advances the iterator. This is simpler than Contains. We have a\n// subiterator we can get a value from, and we can take that resultant quad,\n// pull our direction out of it, and return that.\nfunc (it *hasANext) Next(ctx context.Context) bool {\n\tif !it.primary.Next(ctx) {\n\t\treturn false\n\t}\n\tvar err error\n\tit.result, err = it.qs.QuadDirection(it.primary.Result(), it.dir)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (it *hasANext) Err() error {\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\treturn it.primary.Err()\n}\n\nfunc (it *hasANext) Result() refs.Ref {\n\treturn it.result\n}\n\n// Close the subiterator, the result iterator (if any) and the HasA. It closes\n// all subiterators it can, but returns the first error it encounters.\nfunc (it *hasANext) Close() error {\n\treturn it.primary.Close()\n}\n\n// A HasA consists of a reference back to the graph.QuadStore that it references,\n// a primary subiterator, a direction in which the quads for that subiterator point,\n// and a temporary holder for the iterator generated on Contains().\ntype hasAContains struct {\n\tqs      QuadIndexer\n\tprimary iterator.Index\n\tdir     quad.Direction\n\tresults iterator.Scanner\n\tresult  refs.Ref\n\terr     error\n}\n\n// Construct a new HasA iterator, given the quad subiterator, and the quad\n// direction for which it stands.\nfunc newHasAContains(qs QuadIndexer, subIt iterator.Index, d quad.Direction) iterator.Index {\n\treturn &hasAContains{\n\t\tqs:      qs,\n\t\tprimary: subIt,\n\t\tdir:     d,\n\t}\n}\n\n// Direction accessor.\nfunc (it *hasAContains) Direction() quad.Direction { return it.dir }\n\n// Pass the TagResults down the chain.\nfunc (it *hasAContains) TagResults(dst map[string]refs.Ref) {\n\tit.primary.TagResults(dst)\n}\n\nfunc (it *hasAContains) String() string {\n\treturn fmt.Sprintf(\"HasAContains(%v)\", it.dir)\n}\n\n// Check a value against our internal iterator. In order to do this, we must first open a new\n// iterator of \"quads that have `val` in our direction\", given to us by the quad store,\n// and then Next() values out of that iterator and Contains() them against our subiterator.\nfunc (it *hasAContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tif clog.V(4) {\n\t\tclog.Infof(\"Id is %v\", val)\n\t}\n\t// TODO(barakmich): Optimize this\n\tif it.results != nil {\n\t\tit.results.Close()\n\t}\n\tit.results = it.qs.QuadIterator(it.dir, val).Iterate()\n\tok := it.nextContains(ctx)\n\tif it.err != nil {\n\t\treturn false\n\t}\n\treturn ok\n}\n\n// nextContains() is shared code between Contains() and GetNextResult() -- calls next on the\n// result iterator (a quad iterator based on the last checked value) and returns true if\n// another match is made.\nfunc (it *hasAContains) nextContains(ctx context.Context) bool {\n\tif it.results == nil {\n\t\treturn false\n\t}\n\tfor it.results.Next(ctx) {\n\t\tlink := it.results.Result()\n\t\tif clog.V(4) {\n\t\t\tqlv, err := it.qs.Quad(link)\n\t\t\tif err == nil {\n\t\t\t\tclog.Infof(\"Quad is %v\", qlv)\n\t\t\t} else {\n\t\t\t\tclog.Warningf(\"Error looking up result quad: %v\", err)\n\t\t\t}\n\t\t}\n\t\tif it.primary.Contains(ctx, link) {\n\t\t\tvar err error\n\t\t\tit.result, err = it.qs.QuadDirection(link, it.dir)\n\t\t\tif err != nil {\n\t\t\t\tit.err = err\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t}\n\tit.err = it.results.Err()\n\treturn false\n}\n\n// Get the next result that matches this branch.\nfunc (it *hasAContains) NextPath(ctx context.Context) bool {\n\t// Order here is important. If the subiterator has a NextPath, then we\n\t// need do nothing -- there is a next result, and we shouldn't move forward.\n\t// However, we then need to get the next result from our last Contains().\n\t//\n\t// The upshot is, the end of NextPath() bubbles up from the bottom of the\n\t// iterator tree up, and we need to respect that.\n\tif clog.V(4) {\n\t\tclog.Infof(\"HASA %p NextPath\", it)\n\t}\n\tif it.primary.NextPath(ctx) {\n\t\treturn true\n\t}\n\tit.err = it.primary.Err()\n\tif it.err != nil {\n\t\treturn false\n\t}\n\n\tresult := it.nextContains(ctx) // Sets it.err if there's an error\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif clog.V(4) {\n\t\tclog.Infof(\"HASA %p NextPath Returns %v\", it, result)\n\t}\n\treturn result\n}\n\nfunc (it *hasAContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *hasAContains) Result() refs.Ref {\n\treturn it.result\n}\n\n// Close the subiterator, the result iterator (if any) and the HasA. It closes\n// all subiterators it can, but returns the first error it encounters.\nfunc (it *hasAContains) Close() error {\n\terr := it.primary.Close()\n\tif it.results != nil {\n\t\tif err2 := it.results.Close(); err2 != nil && err == nil {\n\t\t\terr = err2\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "graph/hasa_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc TestHasAIteratorErr(t *testing.T) {\n\twantErr := errors.New(\"unique\")\n\tctx := context.TODO()\n\terrIt := iterator.NewError(wantErr)\n\n\t// TODO(andrew-d): pass a non-nil quadstore\n\thasa := graph.NewHasA(nil, errIt, quad.Subject).Iterate()\n\n\trequire.False(t, hasa.Next(ctx), \"HasA iterator did not pass through initial 'false'\")\n\trequire.Equal(t, wantErr, hasa.Err(), \"HasA iterator did not pass through underlying Err\")\n}\n"
  },
  {
    "path": "graph/http/httpgraph.go",
    "content": "package httpgraph\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n)\n\ntype QuadStore interface {\n\tgraph.QuadStore\n\tForRequest(r *http.Request) (graph.QuadStore, error)\n}\n"
  },
  {
    "path": "graph/iterator/and.go",
    "content": "// Defines the And iterator, one of the base iterators. And requires no\n// knowledge of the constituent QuadStore; its sole purpose is to act as an\n// intersection operator across the subiterators it is given. If one iterator\n// contains [1,3,5] and another [2,3,4] -- then And is an iterator that\n// 'contains' [3]\n//\n// It accomplishes this in one of two ways. If it is a Next()ed iterator (that\n// is, it is a top level iterator, or on the \"Next() path\", then it will Next()\n// it's primary iterator (helpfully, and.primary_it) and Contains() the resultant\n// value against it's other iterators. If it matches all of them, then it\n// returns that value. Otherwise, it repeats the process.\n//\n// If it's on a Contains() path, it merely Contains()s every iterator, and returns the\n// logical AND of each result.\n\npackage iterator\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// The And iterator. Consists of a number of subiterators, the primary of which will\n// be Next()ed if next is called.\ntype And struct {\n\tsub       []Shape\n\tcheckList []Shape // special order for Contains\n\topt       []Shape\n}\n\n// NewAnd creates an And iterator. `qs` is only required when needing a handle\n// for QuadStore-specific optimizations, otherwise nil is acceptable.\nfunc NewAnd(sub ...Shape) *And {\n\tit := &And{\n\t\tsub: make([]Shape, 0, 20),\n\t}\n\tfor _, s := range sub {\n\t\tit.AddSubIterator(s)\n\t}\n\treturn it\n}\n\nfunc (it *And) Iterate() Scanner {\n\tif len(it.sub) == 0 {\n\t\treturn NewNull().Iterate()\n\t}\n\tsub := make([]Index, 0, len(it.sub)-1)\n\tfor _, s := range it.sub[1:] {\n\t\tsub = append(sub, s.Lookup())\n\t}\n\topt := make([]Index, 0, len(it.opt))\n\tfor _, s := range it.opt {\n\t\topt = append(opt, s.Lookup())\n\t}\n\treturn newAndNext(it.sub[0].Iterate(), newAndContains(sub, opt))\n}\n\nfunc (it *And) Lookup() Index {\n\tif len(it.sub) == 0 {\n\t\treturn NewNull().Lookup()\n\t}\n\tsub := make([]Index, 0, len(it.sub))\n\tcheck := it.checkList\n\tif check == nil {\n\t\tcheck = it.sub\n\t}\n\tfor _, s := range check {\n\t\tsub = append(sub, s.Lookup())\n\t}\n\topt := make([]Index, 0, len(it.opt))\n\tfor _, s := range it.opt {\n\t\topt = append(opt, s.Lookup())\n\t}\n\treturn newAndContains(sub, opt)\n}\n\n// Returns a slice of the subiterators, in order (primary iterator first).\nfunc (it *And) SubIterators() []Shape {\n\titers := make([]Shape, 0, len(it.sub)+len(it.opt))\n\titers = append(iters, it.sub...)\n\titers = append(iters, it.opt...)\n\treturn iters\n}\n\nfunc (it *And) String() string {\n\treturn \"And\"\n}\n\n// Add a subiterator to this And iterator.\n//\n// The first iterator that is added becomes the primary iterator. This is\n// important. Calling Optimize() is the way to change the order based on\n// subiterator statistics. Without Optimize(), the order added is the order\n// used.\nfunc (it *And) AddSubIterator(sub Shape) {\n\tif sub == nil {\n\t\tpanic(\"nil iterator\")\n\t}\n\tit.sub = append(it.sub, sub)\n}\n\n// AddOptionalIterator adds an iterator that will only be Contain'ed and will not affect iteration results.\n// Only tags will be propagated from this iterator.\nfunc (it *And) AddOptionalIterator(sub Shape) *And {\n\tit.opt = append(it.opt, sub)\n\treturn it\n}\n\n// The And iterator. Consists of a number of subiterators, the primary of which will\n// be Next()ed if next is called.\ntype andNext struct {\n\tprimary   Scanner\n\tsecondary Index\n\tresult    refs.Ref\n}\n\n// NewAnd creates an And iterator. `qs` is only required when needing a handle\n// for QuadStore-specific optimizations, otherwise nil is acceptable.\nfunc newAndNext(pri Scanner, sec Index) Scanner {\n\treturn &andNext{\n\t\tprimary:   pri,\n\t\tsecondary: sec,\n\t}\n}\n\n// An extended TagResults, as it needs to add it's own results and\n// recurse down it's subiterators.\nfunc (it *andNext) TagResults(dst map[string]refs.Ref) {\n\tit.primary.TagResults(dst)\n\tit.secondary.TagResults(dst)\n}\n\nfunc (it *andNext) String() string {\n\treturn \"AndNext\"\n}\n\n// Returns advances the And iterator. Because the And is the intersection of its\n// subiterators, it must choose one subiterator to produce a candidate, and check\n// this value against the subiterators. A productive choice of primary iterator\n// is therefore very important.\nfunc (it *andNext) Next(ctx context.Context) bool {\n\tfor it.primary.Next(ctx) {\n\t\tcur := it.primary.Result()\n\t\tif it.secondary.Contains(ctx, cur) {\n\t\t\tit.result = cur\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (it *andNext) Err() error {\n\tif err := it.primary.Err(); err != nil {\n\t\treturn err\n\t}\n\tif err := it.secondary.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (it *andNext) Result() refs.Ref {\n\treturn it.result\n}\n\n// An And has no NextPath of its own -- that is, there are no other values\n// which satisfy our previous result that are not the result itself. Our\n// subiterators might, however, so just pass the call recursively.\nfunc (it *andNext) NextPath(ctx context.Context) bool {\n\tif it.primary.NextPath(ctx) {\n\t\treturn true\n\t} else if err := it.primary.Err(); err != nil {\n\t\treturn false\n\t}\n\tif it.secondary.NextPath(ctx) {\n\t\treturn true\n\t} else if err := it.secondary.Err(); err != nil {\n\t\treturn false\n\t}\n\treturn false\n}\n\n// Close this iterator, and, by extension, close the subiterators.\n// Close should be idempotent, and it follows that if it's subiterators\n// follow this contract, the And follows the contract.  It closes all\n// subiterators it can, but returns the first error it encounters.\nfunc (it *andNext) Close() error {\n\terr := it.primary.Close()\n\tif err2 := it.secondary.Close(); err2 != nil && err == nil {\n\t\terr = err2\n\t}\n\treturn err\n}\n\n// The And iterator. Consists of a number of subiterators, the primary of which will\n// be Next()ed if next is called.\ntype andContains struct {\n\tbase     Shape\n\tsub      []Index\n\topt      []Index\n\toptCheck []bool\n\n\tresult refs.Ref\n\terr    error\n}\n\n// NewAnd creates an And iterator. `qs` is only required when needing a handle\n// for QuadStore-specific optimizations, otherwise nil is acceptable.\nfunc newAndContains(sub, opt []Index) Index {\n\treturn &andContains{\n\t\tsub: sub,\n\t\topt: opt, optCheck: make([]bool, len(opt)),\n\t}\n}\n\n// An extended TagResults, as it needs to add it's own results and\n// recurse down it's subiterators.\nfunc (it *andContains) TagResults(dst map[string]refs.Ref) {\n\tfor _, sub := range it.sub {\n\t\tsub.TagResults(dst)\n\t}\n\tfor i, sub := range it.opt {\n\t\tif !it.optCheck[i] {\n\t\t\tcontinue\n\t\t}\n\t\tsub.TagResults(dst)\n\t}\n}\n\nfunc (it *andContains) String() string {\n\treturn \"AndContains\"\n}\n\nfunc (it *andContains) Err() error {\n\tif err := it.err; err != nil {\n\t\treturn err\n\t}\n\tfor _, si := range it.sub {\n\t\tif err := si.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, si := range it.opt {\n\t\tif err := si.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (it *andContains) Result() refs.Ref {\n\treturn it.result\n}\n\n// Check a value against the entire iterator, in order.\nfunc (it *andContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tprev := it.result\n\tfor i, sub := range it.sub {\n\t\tif !sub.Contains(ctx, val) {\n\t\t\tif err := sub.Err(); err != nil {\n\t\t\t\tit.err = err\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// One of the iterators has determined that this value doesn't\n\t\t\t// match. However, the iterators that came before in the list\n\t\t\t// may have returned \"ok\" to Contains().  We need to set all\n\t\t\t// the tags back to what the previous result was -- effectively\n\t\t\t// seeking back exactly one -- so we check all the prior iterators\n\t\t\t// with the (already verified) result and throw away the result,\n\t\t\t// which will be 'true'\n\t\t\tif prev != nil {\n\t\t\t\tfor j := 0; j < i; j++ {\n\t\t\t\t\tit.sub[j].Contains(ctx, prev)\n\t\t\t\t\tif err := it.sub[j].Err(); err != nil {\n\t\t\t\t\t\tit.err = err\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t}\n\tit.result = val\n\tfor i, sub := range it.opt {\n\t\t// remember if we will need to call TagResults on it, nothing more\n\t\tit.optCheck[i] = sub.Contains(ctx, val)\n\t}\n\treturn true\n}\n\n// An And has no NextPath of its own -- that is, there are no other values\n// which satisfy our previous result that are not the result itself. Our\n// subiterators might, however, so just pass the call recursively.\nfunc (it *andContains) NextPath(ctx context.Context) bool {\n\tfor _, sub := range it.sub {\n\t\tif sub.NextPath(ctx) {\n\t\t\treturn true\n\t\t} else if err := sub.Err(); err != nil {\n\t\t\tit.err = err\n\t\t\treturn false\n\t\t}\n\t}\n\tfor i, sub := range it.opt {\n\t\tif !it.optCheck[i] {\n\t\t\tcontinue\n\t\t}\n\t\tif sub.NextPath(ctx) {\n\t\t\treturn true\n\t\t} else if err := sub.Err(); err != nil {\n\t\t\tit.err = err\n\t\t\treturn false\n\t\t}\n\t}\n\treturn false\n}\n\n// Close this iterator, and, by extension, close the subiterators.\n// Close should be idempotent, and it follows that if it's subiterators\n// follow this contract, the And follows the contract.  It closes all\n// subiterators it can, but returns the first error it encounters.\nfunc (it *andContains) Close() error {\n\tvar err error\n\tfor _, sub := range it.sub {\n\t\tif err2 := sub.Close(); err2 != nil && err == nil {\n\t\t\terr = err2\n\t\t}\n\t}\n\tfor _, sub := range it.opt {\n\t\tif err2 := sub.Close(); err2 != nil && err == nil {\n\t\t\terr = err2\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "graph/iterator/and_optimize.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\nimport (\n\t\"context\"\n\t\"sort\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Perhaps the most tricky file in this entire module. Really a method on the\n// And, but important enough to deserve its own file.\n//\n// Calling Optimize() on an And iterator, like any iterator, requires that we\n// preserve the underlying meaning. However, the And has many choices, namely,\n// which one of it's subiterators will be the branch that does the Next()ing,\n// and which ordering of the remaining iterators is the most efficient. In\n// short, this is where a lot of the query optimization happens, and there are\n// many wins to be had here, as well as many bad bugs. The worst class of bug\n// changes the meaning of the query. The second worst class makes things really\n// slow.\n//\n// The good news is this: If Optimize() is never called (turned off, perhaps) we can\n// be sure the results are as good as the query language called for.\n//\n// In short, tread lightly.\n\n// Optimizes the And, by picking the most efficient way to Next() and\n// Contains() its subiterators. For SQL fans, this is equivalent to JOIN.\nfunc (it *And) Optimize(ctx context.Context) (Shape, bool) {\n\t// First, let's get the slice of iterators, in order (first one is Next()ed,\n\t// the rest are Contains()ed)\n\told := it.sub\n\tif len(old) == 0 {\n\t\treturn NewNull(), true\n\t}\n\n\t// And call Optimize() on our subtree, replacing each one in the order we\n\t// found them. it_list is the newly optimized versions of these, and changed\n\t// is another list, of only the ones that have returned replacements and\n\t// changed.\n\tits := optimizeSubIterators(ctx, old)\n\n\t// If we can find only one subiterator which is equivalent to this whole and,\n\t// we can replace the And...\n\tif out := optimizeReplacement(its); out != nil && len(it.opt) == 0 {\n\t\t// ...And return it.\n\t\treturn out, true\n\t}\n\n\t// And now, without changing any of the iterators, we reorder them. it_list is\n\t// now a permutation of itself, but the contents are unchanged.\n\tits = optimizeOrder(ctx, its)\n\n\tits, _ = materializeIts(ctx, its)\n\n\t// Okay! At this point we have an optimized order.\n\n\t// The easiest thing to do at this point is merely to create a new And iterator\n\t// and replace ourselves with our (reordered, optimized) clone.\n\t// Add the subiterators in order.\n\tnewAnd := NewAnd(its...)\n\n\topt := optimizeSubIterators(ctx, it.opt)\n\tfor _, sub := range opt {\n\t\tnewAnd.AddOptionalIterator(sub)\n\t}\n\n\t_ = newAnd.optimizeContains(ctx)\n\tif clog.V(3) {\n\t\tclog.Infof(\"%p become %p\", it, newAnd)\n\t}\n\treturn newAnd, true\n}\n\n// Find if there is a single subiterator which is a valid replacement for this\n// And.\nfunc optimizeReplacement(its []Shape) Shape {\n\t// If we were created with no SubIterators, we're as good as Null.\n\tif len(its) == 0 {\n\t\treturn NewNull()\n\t}\n\tif len(its) == 1 {\n\t\t// When there's only one iterator, there's only one choice.\n\t\treturn its[0]\n\t}\n\t// If any of our subiterators, post-optimization, are also Null, then\n\t// there's no point in continuing the branch, we will have no results\n\t// and we are null as well.\n\tif hasAnyNullIterators(its) {\n\t\treturn NewNull()\n\t}\n\treturn nil\n}\n\n// optimizeOrder(l) takes a list and returns a list, containing the same contents\n// but with a new ordering, however it wishes.\nfunc optimizeOrder(ctx context.Context, its []Shape) []Shape {\n\tvar (\n\t\tbest     Shape\n\t\tbestCost = int64(1 << 62)\n\t)\n\n\t// Find the iterator with the projected \"best\" total cost.\n\t// Total cost is defined as The Next()ed iterator's cost to Next() out\n\t// all of it's contents, and to Contains() each of those against everyone\n\t// else.\n\tcosts := make([]Costs, 0, len(its))\n\tfor _, it := range its {\n\t\tst, _ := it.Stats(ctx)\n\t\tcosts = append(costs, st)\n\t}\n\tfor i, root := range its {\n\t\trootStats := costs[i]\n\t\tcost := rootStats.NextCost\n\t\tfor j, f := range its {\n\t\t\tif f == root {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tstats := costs[j]\n\t\t\tcost += stats.ContainsCost * (1 + (rootStats.Size.Value / (stats.Size.Value + 1)))\n\t\t}\n\t\tcost *= rootStats.Size.Value\n\t\tif clog.V(3) {\n\t\t\tclog.Infof(\"And: Root: %p Total Cost: %v Best: %v\", root, cost, bestCost)\n\t\t}\n\t\tif cost < bestCost {\n\t\t\tbest = root\n\t\t\tbestCost = cost\n\t\t}\n\t}\n\tif clog.V(3) {\n\t\tclog.Infof(\"And: Choosing: %p Best: %v\", best, bestCost)\n\t}\n\n\t// TODO(barakmich): Optimization of order need not stop here. Picking a smart\n\t// Contains() order based on probability of getting a false Contains() first is\n\t// useful (fail faster).\n\n\tvar out []Shape\n\t// Put the best iterator (the one we wish to Next()) at the front...\n\tif best != nil {\n\t\tout = append(out, best)\n\t}\n\n\t// ... push everyone else after...\n\tfor _, it := range its {\n\t\tif it != best {\n\t\t\tout = append(out, it)\n\t\t}\n\t}\n\treturn out\n}\n\nfunc sortByContainsCost(ctx context.Context, arr []Shape) error {\n\tcost := make([]Costs, 0, len(arr))\n\tvar last error\n\tfor _, s := range arr {\n\t\tc, err := s.Stats(ctx)\n\t\tif err != nil {\n\t\t\tlast = err\n\t\t}\n\t\tcost = append(cost, c)\n\t}\n\tsort.Sort(byCost{\n\t\tlist: arr,\n\t\tcost: cost,\n\t})\n\treturn last\n}\n\n// TODO(dennwc): store stats slice once\ntype byCost struct {\n\tlist []Shape\n\tcost []Costs\n}\n\nfunc (c byCost) Len() int { return len(c.list) }\nfunc (c byCost) Less(i, j int) bool {\n\treturn c.cost[i].ContainsCost < c.cost[j].ContainsCost\n}\nfunc (c byCost) Swap(i, j int) {\n\tc.list[i], c.list[j] = c.list[j], c.list[i]\n\tc.cost[i], c.cost[j] = c.cost[j], c.cost[i]\n}\n\n// optimizeContains() creates an alternate check list, containing the same contents\n// but with a new ordering, however it wishes.\nfunc (it *And) optimizeContains(ctx context.Context) error {\n\t// GetSubIterators allocates, so this is currently safe.\n\t// TODO(kortschak) Reuse it.checkList if possible.\n\t// This involves providing GetSubIterators with a slice to fill.\n\t// Generally this is a worthwhile thing to do in other places as well.\n\tit.checkList = append([]Shape{}, it.sub...)\n\treturn sortByContainsCost(ctx, it.checkList)\n}\n\n// optimizeSubIterators(l) takes a list of iterators and calls Optimize() on all\n// of them. It returns two lists -- the first contains the same list as l, where\n// any replacements are made by Optimize() and the second contains the originals\n// which were replaced.\nfunc optimizeSubIterators(ctx context.Context, its []Shape) []Shape {\n\tout := make([]Shape, 0, len(its))\n\tfor _, it := range its {\n\t\to, _ := it.Optimize(ctx)\n\t\tout = append(out, o)\n\t}\n\treturn out\n}\n\n// Check a list of iterators for any Null iterators.\nfunc hasAnyNullIterators(its []Shape) bool {\n\tfor _, it := range its {\n\t\tif IsNull(it) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc materializeIts(ctx context.Context, its []Shape) ([]Shape, error) {\n\tvar out []Shape\n\n\tallStats, stats, err := getStatsForSlice(ctx, its, nil)\n\tout = append(out, its[0])\n\tfor i, it := range its[1:] {\n\t\tst := stats[i+1]\n\t\tif st.Size.Value*st.NextCost < (st.ContainsCost * (1 + (st.Size.Value / (allStats.Size.Value + 1)))) {\n\t\t\tif Height(it, func(it Shape) bool {\n\t\t\t\t_, ok := it.(*Materialize)\n\t\t\t\treturn !ok\n\t\t\t}) > 10 {\n\t\t\t\tout = append(out, NewMaterialize(it))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tout = append(out, it)\n\t}\n\treturn out, err\n}\n\nfunc getStatsForSlice(ctx context.Context, its, opt []Shape) (Costs, []Costs, error) {\n\tif len(its) == 0 {\n\t\treturn Costs{}, nil, nil\n\t}\n\n\tarr := make([]Costs, 0, len(its))\n\n\tprimaryStats, _ := its[0].Stats(ctx)\n\tarr = append(arr, primaryStats)\n\n\tcontainsCost := primaryStats.ContainsCost\n\tnextCost := primaryStats.NextCost\n\tsize := primaryStats.Size.Value\n\texact := primaryStats.Size.Exact\n\n\tvar last error\n\tfor _, sub := range its[1:] {\n\t\tstats, err := sub.Stats(ctx)\n\t\tif err != nil {\n\t\t\tlast = err\n\t\t}\n\t\tarr = append(arr, stats)\n\t\tnextCost += stats.ContainsCost * (1 + (primaryStats.Size.Value / (stats.Size.Value + 1)))\n\t\tcontainsCost += stats.ContainsCost\n\t\tif size > stats.Size.Value {\n\t\t\tsize = stats.Size.Value\n\t\t\texact = stats.Size.Exact\n\t\t}\n\t}\n\tfor _, sub := range opt {\n\t\tstats, _ := sub.Stats(ctx)\n\t\tnextCost += stats.ContainsCost * (1 + (primaryStats.Size.Value / (stats.Size.Value + 1)))\n\t\tcontainsCost += stats.ContainsCost\n\t}\n\treturn Costs{\n\t\tContainsCost: containsCost,\n\t\tNextCost:     nextCost,\n\t\tSize: refs.Size{\n\t\t\tValue: size,\n\t\t\tExact: exact,\n\t\t},\n\t}, arr, last\n}\n\n// Stats lives here in and-iterator-optimize.go because it may\n// in the future return different statistics based on how it is optimized.\n// For now, however, it's pretty static.\n//\n// Returns the approximate size of the And iterator. Because we're dealing\n// with an intersection, we know that the largest we can be is the size of the\n// smallest iterator. This is the heuristic we shall follow. Better heuristics\n// welcome.\nfunc (it *And) Stats(ctx context.Context) (Costs, error) {\n\tstats, _, err := getStatsForSlice(ctx, it.sub, it.opt)\n\treturn stats, err\n}\n"
  },
  {
    "path": "graph/iterator/and_optimize_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\n// Tests relating to methods in and-iterator-optimize. Many are pretty simplistic, but\n// nonetheless cover a lot of basic cases.\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNullIteratorAnd(t *testing.T) {\n\tall := newInt64(1, 3, true)\n\tnull := NewNull()\n\ta := NewAnd(all, null)\n\tnewIt, changed := a.Optimize(context.TODO())\n\tif !changed {\n\t\tt.Error(\"Didn't change\")\n\t}\n\tif _, ok := newIt.(*Null); !ok {\n\t\tt.Errorf(\"Expected null iterator, got %T\", newIt)\n\t}\n}\n\nfunc TestReorderWithTag(t *testing.T) {\n\tall := NewFixed(Int64Node(3))\n\tall2 := NewFixed(\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t\tInt64Node(5),\n\t\tInt64Node(6),\n\t)\n\ta := NewAnd()\n\t// Make all2 the default iterator\n\ta.AddSubIterator(all2)\n\ta.AddSubIterator(all)\n\n\t_, changed := a.Optimize(context.TODO())\n\trequire.True(t, changed, \"expected new iterator\")\n}\n\nfunc TestAndStatistics(t *testing.T) {\n\tall := newInt64(100, 300, true)\n\tall2 := newInt64(1, 30000, true)\n\ta := NewAnd()\n\t// Make all2 the default iterator\n\ta.AddSubIterator(all2)\n\ta.AddSubIterator(all)\n\tctx := context.TODO()\n\tstats1, _ := a.Stats(ctx)\n\tnewIt, changed := a.Optimize(ctx)\n\trequire.True(t, changed, \"didn't optimize\")\n\n\tstats2, _ := newIt.Stats(ctx)\n\tif stats2.NextCost > stats1.NextCost {\n\t\tt.Error(\"And didn't optimize. Next cost old \", stats1.NextCost, \"and new \", stats2.NextCost)\n\t}\n}\n"
  },
  {
    "path": "graph/iterator/and_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Make sure that tags work on the And.\nfunc TestAndTag(t *testing.T) {\n\tctx := context.TODO()\n\tfix1 := NewFixed(Int64Node(234))\n\tfix2 := NewFixed(Int64Node(234))\n\tvar ands Shape = NewAnd(Tag(fix1, \"foo\")).AddOptionalIterator(Tag(fix2, \"baz\"))\n\tands = Tag(ands, \"bar\")\n\n\tand := ands.Iterate()\n\trequire.True(t, and.Next(ctx))\n\trequire.Equal(t, Int64Node(234), and.Result())\n\n\ttags := make(map[string]refs.Ref)\n\tand.TagResults(tags)\n\trequire.Equal(t, map[string]refs.Ref{\n\t\t\"foo\": Int64Node(234),\n\t\t\"bar\": Int64Node(234),\n\t\t\"baz\": Int64Node(234),\n\t}, tags)\n}\n\n// Do a simple itersection of fixed values.\nfunc TestAndAndFixedIterators(t *testing.T) {\n\tctx := context.TODO()\n\tfix1 := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t)\n\tfix2 := NewFixed(\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t\tInt64Node(5),\n\t)\n\tands := NewAnd(fix1, fix2)\n\t// Should be as big as smallest subiterator\n\tst, err := ands.Stats(ctx)\n\trequire.NoError(t, err)\n\trequire.Equal(t, refs.Size{\n\t\tValue: 3,\n\t\tExact: true,\n\t}, st.Size)\n\n\tand := ands.Iterate()\n\n\trequire.True(t, and.Next(ctx))\n\trequire.Equal(t, Int64Node(3), and.Result())\n\n\trequire.True(t, and.Next(ctx))\n\trequire.Equal(t, Int64Node(4), and.Result())\n\n\trequire.False(t, and.Next(ctx))\n}\n\n// If there's no intersection, the size should still report the same,\n// but there should be nothing to Next()\nfunc TestNonOverlappingFixedIterators(t *testing.T) {\n\tctx := context.TODO()\n\tfix1 := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t)\n\tfix2 := NewFixed(\n\t\tInt64Node(5),\n\t\tInt64Node(6),\n\t\tInt64Node(7),\n\t)\n\tands := NewAnd(fix1, fix2)\n\t// Should be as big as smallest subiterator\n\tst, err := ands.Stats(ctx)\n\trequire.NoError(t, err)\n\trequire.Equal(t, refs.Size{\n\t\tValue: 3,\n\t\tExact: true,\n\t}, st.Size)\n\n\tand := ands.Iterate()\n\trequire.False(t, and.Next(ctx))\n}\n\nfunc TestAllIterators(t *testing.T) {\n\tctx := context.TODO()\n\tall1 := newInt64(1, 5, true)\n\tall2 := newInt64(4, 10, true)\n\tand := NewAnd(all2, all1).Iterate()\n\n\trequire.True(t, and.Next(ctx))\n\trequire.Equal(t, Int64Node(4), and.Result())\n\n\trequire.True(t, and.Next(ctx))\n\trequire.Equal(t, Int64Node(5), and.Result())\n\n\trequire.False(t, and.Next(ctx))\n}\n\nfunc TestAndIteratorErr(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\tallErr := newTestIterator(false, wantErr)\n\n\tand := NewAnd(\n\t\tallErr,\n\t\tnewInt64(1, 5, true),\n\t).Iterate()\n\n\trequire.False(t, and.Next(ctx))\n\trequire.Equal(t, wantErr, and.Err())\n}\n"
  },
  {
    "path": "graph/iterator/count.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// Count iterator returns one element with size of underlying iterator.\ntype Count struct {\n\tit Shape\n\tqs refs.Namer\n}\n\n// NewCount creates a new iterator to count a number of results from a provided subiterator.\n// qs may be nil - it's used to check if count Contains (is) a given value.\nfunc NewCount(it Shape, qs refs.Namer) *Count {\n\treturn &Count{\n\t\tit: it, qs: qs,\n\t}\n}\n\nfunc (it *Count) Iterate() Scanner {\n\treturn newCountNext(it.it)\n}\n\nfunc (it *Count) Lookup() Index {\n\treturn newCountContains(it.it, it.qs)\n}\n\n// SubIterators returns a slice of the sub iterators.\nfunc (it *Count) SubIterators() []Shape {\n\treturn []Shape{it.it}\n}\n\nfunc (it *Count) Optimize(ctx context.Context) (Shape, bool) {\n\tsub, optimized := it.it.Optimize(ctx)\n\tit.it = sub\n\treturn it, optimized\n}\n\nfunc (it *Count) Stats(ctx context.Context) (Costs, error) {\n\tstats := Costs{\n\t\tNextCost: 1,\n\t\tSize: refs.Size{\n\t\t\tValue: 1,\n\t\t\tExact: true,\n\t\t},\n\t}\n\tif sub, err := it.it.Stats(ctx); err == nil && !sub.Size.Exact {\n\t\tstats.NextCost = sub.NextCost * sub.Size.Value\n\t}\n\tstats.ContainsCost = stats.NextCost\n\treturn stats, nil\n}\n\nfunc (it *Count) String() string { return \"Count\" }\n\n// Count iterator returns one element with size of underlying iterator.\ntype countNext struct {\n\tit     Shape\n\tdone   bool\n\tresult quad.Value\n\terr    error\n}\n\n// NewCount creates a new iterator to count a number of results from a provided subiterator.\n// qs may be nil - it's used to check if count Contains (is) a given value.\nfunc newCountNext(it Shape) *countNext {\n\treturn &countNext{\n\t\tit: it,\n\t}\n}\n\nfunc (it *countNext) TagResults(dst map[string]refs.Ref) {}\n\n// Next counts a number of results in underlying iterator.\nfunc (it *countNext) Next(ctx context.Context) bool {\n\tif it.done {\n\t\treturn false\n\t}\n\t// TODO(dennwc): this most likely won't include the NextPath\n\tst, err := it.it.Stats(ctx)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tif !st.Size.Exact {\n\t\tsit := it.it.Iterate()\n\t\tdefer sit.Close()\n\t\tfor st.Size.Value = 0; sit.Next(ctx); st.Size.Value++ {\n\t\t\t// TODO(dennwc): it's unclear if we should call it here or not\n\t\t\tfor ; sit.NextPath(ctx); st.Size.Value++ {\n\t\t\t}\n\t\t}\n\t\tit.err = sit.Err()\n\t}\n\tit.result = quad.Int(st.Size.Value)\n\tit.done = true\n\treturn true\n}\n\nfunc (it *countNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *countNext) Result() refs.Ref {\n\tif it.result == nil {\n\t\treturn nil\n\t}\n\treturn refs.PreFetched(it.result)\n}\n\nfunc (it *countNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *countNext) Close() error {\n\treturn nil\n}\n\nfunc (it *countNext) String() string { return \"CountNext\" }\n\n// Count iterator returns one element with size of underlying iterator.\ntype countContains struct {\n\tit  *countNext\n\tqs  refs.Namer\n\terr error\n}\n\n// NewCount creates a new iterator to count a number of results from a provided subiterator.\n// qs may be nil - it's used to check if count Contains (is) a given value.\nfunc newCountContains(it Shape, qs refs.Namer) *countContains {\n\treturn &countContains{\n\t\tit: newCountNext(it),\n\t\tqs: qs,\n\t}\n}\n\nfunc (it *countContains) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *countContains) Err() error {\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\treturn it.it.Err()\n}\n\nfunc (it *countContains) Result() refs.Ref {\n\treturn it.it.Result()\n}\n\nfunc (it *countContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tif !it.it.done {\n\t\tit.it.Next(ctx)\n\t}\n\tif v, ok := val.(refs.PreFetchedValue); ok {\n\t\treturn v.NameOf() == it.it.result\n\t}\n\tif it.qs != nil {\n\t\tvalName, err := it.qs.NameOf(val)\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t\treturn false\n\t\t}\n\t\treturn valName == it.it.result\n\t}\n\treturn false\n}\n\nfunc (it *countContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *countContains) Close() error {\n\treturn it.it.Close()\n}\n\nfunc (it *countContains) String() string { return \"CountContains\" }\n"
  },
  {
    "path": "graph/iterator/count_test.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCount(t *testing.T) {\n\tctx := context.TODO()\n\tfixed := NewFixed(\n\t\trefs.PreFetched(quad.String(\"a\")),\n\t\trefs.PreFetched(quad.String(\"b\")),\n\t\trefs.PreFetched(quad.String(\"c\")),\n\t\trefs.PreFetched(quad.String(\"d\")),\n\t\trefs.PreFetched(quad.String(\"e\")),\n\t)\n\tits := NewCount(fixed, nil)\n\n\titn := its.Iterate()\n\trequire.True(t, itn.Next(ctx))\n\trequire.Equal(t, refs.PreFetched(quad.Int(5)), itn.Result())\n\trequire.False(t, itn.Next(ctx))\n\n\titc := its.Lookup()\n\trequire.True(t, itc.Contains(ctx, refs.PreFetched(quad.Int(5))))\n\trequire.False(t, itc.Contains(ctx, refs.PreFetched(quad.Int(3))))\n\n\tfixed2 := NewFixed(\n\t\trefs.PreFetched(quad.String(\"b\")),\n\t\trefs.PreFetched(quad.String(\"d\")),\n\t)\n\tits = NewCount(NewAnd(fixed, fixed2), nil)\n\n\titn = its.Iterate()\n\trequire.True(t, itn.Next(ctx))\n\trequire.Equal(t, refs.PreFetched(quad.Int(2)), itn.Result())\n\trequire.False(t, itn.Next(ctx))\n\n\titc = its.Lookup()\n\trequire.False(t, itc.Contains(ctx, refs.PreFetched(quad.Int(5))))\n\trequire.True(t, itc.Contains(ctx, refs.PreFetched(quad.Int(2))))\n}\n"
  },
  {
    "path": "graph/iterator/fixed.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// Defines one of the base iterators, the Fixed iterator. A fixed iterator is quite simple; it\n// contains an explicit fixed array of values.\n//\n// A fixed iterator requires an Equality function to be passed to it, by reason that refs.Ref, the\n// opaque Quad store value, may not answer to ==.\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nvar _ Shape = &Fixed{}\n\n// A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and\n// an equality function.\ntype Fixed struct {\n\tvalues []refs.Ref\n}\n\n// NewFixed creates a new Fixed iterator with a custom comparator.\nfunc NewFixed(vals ...refs.Ref) *Fixed {\n\treturn &Fixed{\n\t\tvalues: append([]refs.Ref{}, vals...),\n\t}\n}\n\nfunc (it *Fixed) Iterate() Scanner {\n\treturn newFixedNext(it.values)\n}\n\nfunc (it *Fixed) Lookup() Index {\n\treturn newFixedContains(it.values)\n}\n\n// Add a value to the iterator. The array now contains this value.\n// TODO(barakmich): This ought to be a set someday, disallowing repeated values.\nfunc (it *Fixed) Add(v refs.Ref) {\n\tit.values = append(it.values, v)\n}\n\n// Values returns a list of values stored in iterator. Slice must not be modified.\nfunc (it *Fixed) Values() []refs.Ref {\n\treturn it.values\n}\n\nfunc (it *Fixed) String() string {\n\treturn fmt.Sprintf(\"Fixed(%v)\", it.values)\n}\n\n// No sub-iterators.\nfunc (it *Fixed) SubIterators() []Shape {\n\treturn nil\n}\n\n// Optimize() for a Fixed iterator is simple. Returns a Null iterator if it's empty\n// (so that other iterators upstream can treat this as null) or there is no\n// optimization.\nfunc (it *Fixed) Optimize(ctx context.Context) (Shape, bool) {\n\tif len(it.values) == 1 && it.values[0] == nil {\n\t\treturn NewNull(), true\n\t}\n\n\treturn it, false\n}\n\n// As we right now have to scan the entire list, Next and Contains are linear with the\n// size. However, a better data structure could remove these limits.\nfunc (it *Fixed) Stats(ctx context.Context) (Costs, error) {\n\treturn Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     1,\n\t\tSize: refs.Size{\n\t\t\tValue: int64(len(it.values)),\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\n// A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and\n// an equality function.\ntype fixedNext struct {\n\tvalues []refs.Ref\n\tind    int\n\tresult refs.Ref\n}\n\n// Creates a new Fixed iterator with a custom comparator.\nfunc newFixedNext(vals []refs.Ref) *fixedNext {\n\treturn &fixedNext{\n\t\tvalues: vals,\n\t}\n}\n\nfunc (it *fixedNext) Close() error {\n\treturn nil\n}\n\nfunc (it *fixedNext) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *fixedNext) String() string {\n\treturn fmt.Sprintf(\"Fixed(%v)\", it.values)\n}\n\n// Next advances the iterator.\nfunc (it *fixedNext) Next(ctx context.Context) bool {\n\tif it.ind >= len(it.values) {\n\t\treturn false\n\t}\n\tout := it.values[it.ind]\n\tit.result = out\n\tit.ind++\n\treturn true\n}\n\nfunc (it *fixedNext) Err() error {\n\treturn nil\n}\n\nfunc (it *fixedNext) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *fixedNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and\n// an equality function.\ntype fixedContains struct {\n\tvalues []refs.Ref\n\tkeys   []interface{}\n\tresult refs.Ref\n}\n\n// Creates a new Fixed iterator with a custom comparator.\nfunc newFixedContains(vals []refs.Ref) *fixedContains {\n\tkeys := make([]interface{}, 0, len(vals))\n\tfor _, v := range vals {\n\t\tkeys = append(keys, refs.ToKey(v))\n\t}\n\treturn &fixedContains{\n\t\tvalues: vals,\n\t\tkeys:   keys,\n\t}\n}\n\nfunc (it *fixedContains) Close() error {\n\treturn nil\n}\n\nfunc (it *fixedContains) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *fixedContains) String() string {\n\treturn fmt.Sprintf(\"Fixed(%v)\", it.values)\n}\n\n// Check if the passed value is equal to one of the values stored in the iterator.\nfunc (it *fixedContains) Contains(ctx context.Context, v refs.Ref) bool {\n\t// Could be optimized by keeping it sorted or using a better datastructure.\n\t// However, for fixed iterators, which are by definition kind of tiny, this\n\t// isn't a big issue.\n\tvk := refs.ToKey(v)\n\tfor i, x := range it.keys {\n\t\tif x == vk {\n\t\t\tit.result = it.values[i]\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (it *fixedContains) Err() error {\n\treturn nil\n}\n\nfunc (it *fixedContains) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *fixedContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n"
  },
  {
    "path": "graph/iterator/iterate.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// Chain is a chain-enabled helper to setup iterator execution.\ntype Chain struct {\n\tctx context.Context\n\ts   Shape\n\tit  Scanner\n\tqs  refs.Namer\n\n\tpaths    bool\n\toptimize bool\n\n\tlimit int\n\tn     int\n}\n\n// Iterate is a set of helpers for iteration. Context may be used to cancel execution.\n// Iterator will be optimized and closed after execution.\n//\n// By default, iteration has no limit and includes sub-paths.\nfunc Iterate(ctx context.Context, it Shape) *Chain {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\treturn &Chain{\n\t\tctx: ctx, s: it,\n\t\tlimit: -1, paths: true,\n\t\toptimize: true,\n\t}\n}\nfunc (c *Chain) next() bool {\n\tselect {\n\tcase <-c.ctx.Done():\n\t\treturn false\n\tdefault:\n\t}\n\tok := (c.limit < 0 || c.n < c.limit) && c.it.Next(c.ctx)\n\tif ok {\n\t\tc.n++\n\t}\n\treturn ok\n}\nfunc (c *Chain) nextPath() bool {\n\tselect {\n\tcase <-c.ctx.Done():\n\t\treturn false\n\tdefault:\n\t}\n\tok := c.paths && (c.limit < 0 || c.n < c.limit) && c.it.NextPath(c.ctx)\n\tif ok {\n\t\tc.n++\n\t}\n\treturn ok\n}\nfunc (c *Chain) start() {\n\tif c.optimize {\n\t\tc.s, _ = c.s.Optimize(c.ctx)\n\t}\n\tc.it = c.s.Iterate()\n}\n\nfunc (c *Chain) end() {\n\tc.it.Close()\n}\n\n// Limit limits a total number of results returned.\nfunc (c *Chain) Limit(n int) *Chain {\n\tc.limit = n\n\treturn c\n}\n\n// Paths switches iteration over sub-paths (with it.NextPath).\n// Defaults to true.\nfunc (c *Chain) Paths(enable bool) *Chain {\n\tc.paths = enable\n\treturn c\n}\n\n// On sets a default quad store for iteration. If qs was set, it may be omitted in other functions.\nfunc (c *Chain) On(qs refs.Namer) *Chain {\n\tc.qs = qs\n\treturn c\n}\n\n// UnOptimized disables iterator optimization.\nfunc (c *Chain) UnOptimized() *Chain {\n\tc.optimize = false\n\treturn c\n}\n\n// Each will run a provided callback for each result of the iterator.\nfunc (c *Chain) Each(fnc func(refs.Ref) error) error {\n\tc.start()\n\tdefer c.end()\n\tdone := c.ctx.Done()\n\n\tfor c.next() {\n\t\tselect {\n\t\tcase <-done:\n\t\t\treturn c.ctx.Err()\n\t\tdefault:\n\t\t}\n\t\terr := fnc(c.it.Result())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor c.nextPath() {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\treturn c.ctx.Err()\n\t\t\tdefault:\n\t\t\t}\n\t\t\tfnc(c.it.Result())\n\t\t}\n\t}\n\treturn c.it.Err()\n}\n\n// All will return all results of an iterator.\nfunc (c *Chain) Count() (int64, error) {\n\t// TODO(dennwc): this should wrap the shape in Count\n\tif c.optimize {\n\t\tc.s, _ = c.s.Optimize(c.ctx)\n\t}\n\tif st, err := c.s.Stats(c.ctx); err != nil {\n\t\treturn st.Size.Value, err\n\t} else if st.Size.Exact {\n\t\treturn st.Size.Value, nil\n\t}\n\tc.start()\n\tdefer c.end()\n\tif err := c.it.Err(); err != nil {\n\t\treturn 0, err\n\t}\n\tdone := c.ctx.Done()\n\tvar cnt int64\niteration:\n\tfor c.next() {\n\t\tselect {\n\t\tcase <-done:\n\t\t\tbreak iteration\n\t\tdefault:\n\t\t}\n\t\tcnt++\n\t\tfor c.nextPath() {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\tbreak iteration\n\t\t\tdefault:\n\t\t\t}\n\t\t\tcnt++\n\t\t}\n\t}\n\treturn cnt, c.it.Err()\n}\n\n// All will return all results of an iterator.\nfunc (c *Chain) All() ([]refs.Ref, error) {\n\tc.start()\n\tdefer c.end()\n\tdone := c.ctx.Done()\n\tvar out []refs.Ref\niteration:\n\tfor c.next() {\n\t\tselect {\n\t\tcase <-done:\n\t\t\tbreak iteration\n\t\tdefault:\n\t\t}\n\t\tout = append(out, c.it.Result())\n\t\tfor c.nextPath() {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\tbreak iteration\n\t\t\tdefault:\n\t\t\t}\n\t\t\tout = append(out, c.it.Result())\n\t\t}\n\t}\n\treturn out, c.it.Err()\n}\n\n// First will return a first result of an iterator. It returns nil if iterator is empty.\nfunc (c *Chain) First() (refs.Ref, error) {\n\tc.start()\n\tdefer c.end()\n\tif !c.next() {\n\t\treturn nil, c.it.Err()\n\t}\n\treturn c.it.Result(), nil\n}\n\n// Send will send each result of the iterator to the provided channel.\n//\n// Channel will NOT be closed when function returns.\nfunc (c *Chain) Send(out chan<- refs.Ref) error {\n\tc.start()\n\tdefer c.end()\n\tdone := c.ctx.Done()\n\tfor c.next() {\n\t\tselect {\n\t\tcase <-done:\n\t\t\treturn c.ctx.Err()\n\t\tcase out <- c.it.Result():\n\t\t}\n\t\tfor c.nextPath() {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\treturn c.ctx.Err()\n\t\t\tcase out <- c.it.Result():\n\t\t\t}\n\t\t}\n\t}\n\treturn c.it.Err()\n}\n\n// TagEach will run a provided tag map callback for each result of the iterator.\nfunc (c *Chain) TagEach(fnc func(map[string]refs.Ref) error) error {\n\tc.start()\n\tdefer c.end()\n\tdone := c.ctx.Done()\n\n\tmn := 0\n\tfor c.next() {\n\t\tselect {\n\t\tcase <-done:\n\t\t\treturn c.ctx.Err()\n\t\tdefault:\n\t\t}\n\t\ttags := make(map[string]refs.Ref, mn)\n\t\tc.it.TagResults(tags)\n\t\tif n := len(tags); n > mn {\n\t\t\tmn = n\n\t\t}\n\t\terr := fnc(tags)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor c.nextPath() {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\treturn c.ctx.Err()\n\t\t\tdefault:\n\t\t\t}\n\t\t\ttags := make(map[string]refs.Ref, mn)\n\t\t\tc.it.TagResults(tags)\n\t\t\tif n := len(tags); n > mn {\n\t\t\t\tmn = n\n\t\t\t}\n\t\t\terr = fnc(tags)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn c.it.Err()\n}\n\nvar errNoQuadStore = fmt.Errorf(\"no quad store in Iterate\")\n\n// EachValue is an analog of Each, but it will additionally call NameOf\n// for each graph.Ref before passing it to a callback.\nfunc (c *Chain) EachValue(qs refs.Namer, fnc func(quad.Value) error) error {\n\tif qs != nil {\n\t\tc.qs = qs\n\t}\n\tif c.qs == nil {\n\t\treturn errNoQuadStore\n\t}\n\t// TODO(dennwc): batch NameOf?\n\treturn c.Each(func(v refs.Ref) error {\n\t\tnv, err := c.qs.NameOf(v)\n\t\tif err == nil && nv != nil {\n\t\t\terr = fnc(nv)\n\t\t}\n\t\treturn err\n\t})\n}\n\n// EachValuePair is an analog of Each, but it will additionally call NameOf\n// for each graph.Ref before passing it to a callback. Original value will be passed as well.\nfunc (c *Chain) EachValuePair(qs refs.Namer, fnc func(refs.Ref, quad.Value) error) error {\n\tif qs != nil {\n\t\tc.qs = qs\n\t}\n\tif c.qs == nil {\n\t\treturn errNoQuadStore\n\t}\n\t// TODO(dennwc): batch NameOf?\n\treturn c.Each(func(v refs.Ref) error {\n\t\tnv, err := c.qs.NameOf(v)\n\t\tif err == nil && nv != nil {\n\t\t\terr = fnc(v, nv)\n\t\t}\n\t\treturn err\n\t})\n}\n\n// AllValues is an analog of All, but it will additionally call NameOf\n// for each graph.Ref before returning the results slice.\nfunc (c *Chain) AllValues(qs refs.Namer) ([]quad.Value, error) {\n\tvar out []quad.Value\n\terr := c.EachValue(qs, func(v quad.Value) error {\n\t\tout = append(out, v)\n\t\treturn nil\n\t})\n\treturn out, err\n}\n\n// FirstValue is an analog of First, but it does lookup of a value in QuadStore.\nfunc (c *Chain) FirstValue(qs refs.Namer) (quad.Value, error) {\n\tif qs != nil {\n\t\tc.qs = qs\n\t}\n\tif c.qs == nil {\n\t\treturn nil, errNoQuadStore\n\t}\n\tv, err := c.First()\n\tif err != nil || v == nil {\n\t\treturn nil, err\n\t}\n\treturn c.qs.NameOf(v)\n}\n\n// SendValues is an analog of Send, but it will additionally call NameOf\n// for each graph.Ref before sending it to a channel.\nfunc (c *Chain) SendValues(qs refs.Namer, out chan<- quad.Value) error {\n\tif qs != nil {\n\t\tc.qs = qs\n\t}\n\tif c.qs == nil {\n\t\treturn errNoQuadStore\n\t}\n\tc.start()\n\tdefer c.end()\n\tdone := c.ctx.Done()\n\tsend := func(v refs.Ref) error {\n\t\tnv, err := c.qs.NameOf(c.it.Result())\n\t\tif err != nil || nv == nil {\n\t\t\treturn err\n\t\t}\n\t\tnvResult, err := c.qs.NameOf(c.it.Result())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tselect {\n\t\tcase <-done:\n\t\t\treturn c.ctx.Err()\n\t\tcase out <- nvResult:\n\t\t}\n\t\treturn nil\n\t}\n\tfor c.next() {\n\t\tif err := send(c.it.Result()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor c.nextPath() {\n\t\t\tif err := send(c.it.Result()); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn c.it.Err()\n}\n\n// TagValues is an analog of TagEach, but it will additionally call NameOf\n// for each graph.Ref before passing the map to a callback.\nfunc (c *Chain) TagValues(qs refs.Namer, fnc func(map[string]quad.Value) error) error {\n\tif qs != nil {\n\t\tc.qs = qs\n\t}\n\tif c.qs == nil {\n\t\treturn errNoQuadStore\n\t}\n\treturn c.TagEach(func(m map[string]refs.Ref) error {\n\t\tvm := make(map[string]quad.Value, len(m))\n\t\tfor k, v := range m {\n\t\t\tvar err error\n\t\t\tvm[k], err = c.qs.NameOf(v) // TODO(dennwc): batch NameOf?\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tfnc(vm)\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "graph/iterator/iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// Define the general iterator interface.\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nvar (\n\t_ Shape = &Null{}\n\t_ Shape = &Error{}\n)\n\n// TaggerBase is a base interface for Tagger and TaggerShape.\ntype TaggerBase interface {\n\tTags() []string\n\tFixedTags() map[string]refs.Ref\n\tAddTags(tag ...string)\n\tAddFixedTag(tag string, value refs.Ref)\n}\n\n// Base is a set of common methods for Scanner and Index iterators.\ntype Base interface {\n\t// String returns a short textual representation of an iterator.\n\tString() string\n\n\t// Fills a tag-to-result-value map.\n\tTagResults(map[string]refs.Ref)\n\n\t// Returns the current result.\n\tResult() refs.Ref\n\n\t// These methods are the heart and soul of the iterator, as they constitute\n\t// the iteration interface.\n\t//\n\t// To get the full results of iteration, do the following:\n\t//\n\t//  for it.Next(ctx) {\n\t//  \tval := it.Result()\n\t//  \t... do things with val.\n\t//  \tfor it.NextPath(ctx) {\n\t//  \t\t... find other paths to iterate\n\t//  \t}\n\t//  }\n\t//\n\t// All of them should set iterator.result to be the last returned value, to\n\t// make results work.\n\t//\n\t// NextPath() advances iterators that may have more than one valid result,\n\t// from the bottom up.\n\tNextPath(ctx context.Context) bool\n\n\t// Err returns any error that was encountered by the Iterator.\n\tErr() error\n\n\t// TODO: make a requirement that Err should return ErrClosed after Close is called\n\n\t// Close the iterator and do internal cleanup.\n\tClose() error\n}\n\n// Scanner is an iterator that lists all results sequentially, but not necessarily in a sorted order.\ntype Scanner interface {\n\tBase\n\n\t// Next advances the iterator to the next value, which will then be available through\n\t// the Result method. It returns false if no further advancement is possible, or if an\n\t// error was encountered during iteration.  Err should be consulted to distinguish\n\t// between the two cases.\n\tNext(ctx context.Context) bool\n}\n\n// Index is an index lookup iterator. It allows to check if an index contains a specific value.\ntype Index interface {\n\tBase\n\n\t// Contains returns whether the value is within the set held by the iterator.\n\t//\n\t// It will set Result to the matching subtree. TagResults can be used to collect values from tree branches.\n\tContains(ctx context.Context, v refs.Ref) bool\n}\n\n// TaggerShape is an interface for iterators that can tag values. Tags are returned as a part of TagResults call.\ntype TaggerShape interface {\n\tShape\n\tTaggerBase\n\tCopyFromTagger(st TaggerBase)\n}\n\ntype Costs struct {\n\tContainsCost int64\n\tNextCost     int64\n\tSize         refs.Size\n}\n\n// Shape is an iterator shape, similar to a query plan. But the plan is not specific in this\n// case - it is used to reorder query branches, and the decide what branches will be scanned\n// and what branches will lookup values (hopefully from the index, but not necessarily).\ntype Shape interface {\n\t// TODO(dennwc): merge with shape.Shape\n\n\t// String returns a short textual representation of an iterator.\n\tString() string\n\n\t// Iterate starts this iterator in scanning mode. Resulting iterator will list all\n\t// results sequentially, but not necessary in the sorted order. Caller must close\n\t// the iterator.\n\tIterate() Scanner\n\n\t// Lookup starts this iterator in an index lookup mode. Depending on the iterator type,\n\t// this may still involve database scans. Resulting iterator allows to check an index\n\t// contains a specified value. Caller must close the iterator.\n\tLookup() Index\n\n\t// These methods relate to choosing the right iterator, or optimizing an\n\t// iterator tree\n\t//\n\t// Stats() returns the relative costs of calling the iteration methods for\n\t// this iterator, as well as the size. Roughly, it will take NextCost * Size\n\t// \"cost units\" to get everything out of the iterator. This is a wibbly-wobbly\n\t// thing, and not exact, but a useful heuristic.\n\tStats(ctx context.Context) (Costs, error)\n\n\t// Optimizes an iterator. Can replace the iterator, or merely move things\n\t// around internally. if it chooses to replace it with a better iterator,\n\t// returns (the new iterator, true), if not, it returns (self, false).\n\tOptimize(ctx context.Context) (Shape, bool)\n\n\t// Return a slice of the subiterators for this iterator.\n\tSubIterators() []Shape\n}\n\ntype Morphism func(Shape) Shape\n\nfunc IsNull(it Shape) bool {\n\tif _, ok := it.(*Null); ok {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Height is a convienence function to measure the height of an iterator tree.\nfunc Height(it Shape, filter func(Shape) bool) int {\n\tif filter != nil && !filter(it) {\n\t\treturn 1\n\t}\n\tsubs := it.SubIterators()\n\tmaxDepth := 0\n\tfor _, sub := range subs {\n\t\th := Height(sub, filter)\n\t\tif h > maxDepth {\n\t\t\tmaxDepth = h\n\t\t}\n\t}\n\treturn maxDepth + 1\n}\n\n// Null is the simplest iterator -- the Null iterator. It contains nothing.\n// It is the empty set. Often times, queries that contain one of these match nothing,\n// so it's important to give it a special iterator.\ntype Null struct{}\n\n// NewNull creates a new Null iterator\n// Fairly useless New function.\nfunc NewNull() *Null {\n\treturn &Null{}\n}\n\n// Iterate implements Iterator\nfunc (it *Null) Iterate() Scanner {\n\treturn it\n}\n\n// Lookup implements Iterator\nfunc (it *Null) Lookup() Index {\n\treturn it\n}\n\n// Fill the map based on the tags assigned to this iterator.\nfunc (it *Null) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *Null) Contains(ctx context.Context, v refs.Ref) bool {\n\treturn false\n}\n\n// A good iterator will close itself when it returns true.\n// Null has nothing it needs to do.\nfunc (it *Null) Optimize(ctx context.Context) (Shape, bool) { return it, false }\n\nfunc (it *Null) String() string {\n\treturn \"Null\"\n}\n\nfunc (it *Null) Next(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *Null) Err() error {\n\treturn nil\n}\n\nfunc (it *Null) Result() refs.Ref {\n\treturn nil\n}\n\nfunc (it *Null) SubIterators() []Shape {\n\treturn nil\n}\n\nfunc (it *Null) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *Null) Reset() {}\n\nfunc (it *Null) Close() error {\n\treturn nil\n}\n\n// A null iterator costs nothing. Use it!\nfunc (it *Null) Stats(ctx context.Context) (Costs, error) {\n\treturn Costs{}, nil\n}\n\n// Error iterator always returns a single error with no other results.\ntype Error struct {\n\terr error\n}\n\nfunc NewError(err error) *Error {\n\treturn &Error{err: err}\n}\n\nfunc (it *Error) Iterate() Scanner {\n\treturn it\n}\n\nfunc (it *Error) Lookup() Index {\n\treturn it\n}\n\n// Fill the map based on the tags assigned to this iterator.\nfunc (it *Error) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *Error) Contains(ctx context.Context, v refs.Ref) bool {\n\treturn false\n}\n\nfunc (it *Error) Optimize(ctx context.Context) (Shape, bool) { return it, false }\n\nfunc (it *Error) String() string {\n\treturn fmt.Sprintf(\"Error(%v)\", it.err)\n}\n\nfunc (it *Error) Next(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *Error) Err() error {\n\treturn it.err\n}\n\nfunc (it *Error) Result() refs.Ref {\n\treturn nil\n}\n\nfunc (it *Error) SubIterators() []Shape {\n\treturn nil\n}\n\nfunc (it *Error) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *Error) Reset() {}\n\nfunc (it *Error) Close() error {\n\treturn it.err\n}\n\nfunc (it *Error) Stats(ctx context.Context) (Costs, error) {\n\treturn Costs{}, nil\n}\n"
  },
  {
    "path": "graph/iterator/iterator_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// A testing iterator that returns the given values for Next() and Err().\ntype testIterator struct {\n\tShape\n\n\tNextVal bool\n\tErrVal  error\n}\n\nfunc newTestIterator(next bool, err error) Shape {\n\treturn &testIterator{\n\t\tShape:   NewFixed(),\n\t\tNextVal: next,\n\t\tErrVal:  err,\n\t}\n}\n\nfunc (it *testIterator) Iterate() Scanner {\n\treturn &testIteratorNext{\n\t\tScanner: it.Shape.Iterate(),\n\t\tNextVal: it.NextVal,\n\t\tErrVal:  it.ErrVal,\n\t}\n}\n\nfunc (it *testIterator) Lookup() Index {\n\treturn &testIteratorContains{\n\t\tIndex:   it.Shape.Lookup(),\n\t\tNextVal: it.NextVal,\n\t\tErrVal:  it.ErrVal,\n\t}\n}\n\n// A testing iterator that returns the given values for Next() and Err().\ntype testIteratorNext struct {\n\tScanner\n\n\tNextVal bool\n\tErrVal  error\n}\n\nfunc (it *testIteratorNext) Next(ctx context.Context) bool {\n\treturn it.NextVal\n}\n\nfunc (it *testIteratorNext) Err() error {\n\treturn it.ErrVal\n}\n\n// A testing iterator that returns the given values for Next() and Err().\ntype testIteratorContains struct {\n\tIndex\n\n\tNextVal bool\n\tErrVal  error\n}\n\nfunc (it *testIteratorContains) Contains(ctx context.Context, v refs.Ref) bool {\n\treturn it.NextVal\n}\n\nfunc (it *testIteratorContains) Err() error {\n\treturn it.ErrVal\n}\n\ntype Int64Quad int64\n\nfunc (v Int64Quad) Key() interface{} { return v }\n\nfunc (Int64Quad) IsNode() bool { return false }\n\nvar _ Shape = &Int64{}\n\n// An All iterator across a range of int64 values, from `max` to `min`.\ntype Int64 struct {\n\tnode     bool\n\tmax, min int64\n}\n\nfunc (it *Int64) Iterate() Scanner {\n\treturn newInt64Next(it.min, it.max, it.node)\n}\n\nfunc (it *Int64) Lookup() Index {\n\treturn newInt64Contains(it.min, it.max, it.node)\n}\n\n// Creates a new Int64 with the given range.\nfunc newInt64(min, max int64, node bool) *Int64 {\n\treturn &Int64{\n\t\tnode: node,\n\t\tmin:  min,\n\t\tmax:  max,\n\t}\n}\n\nfunc (it *Int64) String() string {\n\treturn fmt.Sprintf(\"Int64(%d-%d)\", it.min, it.max)\n}\n\n// No sub-iterators.\nfunc (it *Int64) SubIterators() []Shape {\n\treturn nil\n}\n\n// The number of elements in an Int64 is the size of the range.\n// The size is exact.\nfunc (it *Int64) Size() (int64, bool) {\n\tsz := (it.max - it.min) + 1\n\treturn sz, true\n}\n\nfunc valToInt64(v refs.Ref) int64 {\n\tif v, ok := v.(Int64Node); ok {\n\t\treturn int64(v)\n\t}\n\treturn int64(v.(Int64Quad))\n}\n\n// There's nothing to optimize about this little iterator.\nfunc (it *Int64) Optimize(ctx context.Context) (Shape, bool) { return it, false }\n\n// Stats for an Int64 are simple. Super cheap to do any operation,\n// and as big as the range.\nfunc (it *Int64) Stats(ctx context.Context) (Costs, error) {\n\ts, exact := it.Size()\n\treturn Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     1,\n\t\tSize: refs.Size{\n\t\t\tValue: s,\n\t\t\tExact: exact,\n\t\t},\n\t}, nil\n}\n\n// An All iterator across a range of int64 values, from `max` to `min`.\ntype int64Next struct {\n\tnode     bool\n\tmax, min int64\n\tat       int64\n\tresult   int64\n}\n\n// Creates a new Int64 with the given range.\nfunc newInt64Next(min, max int64, node bool) *int64Next {\n\treturn &int64Next{\n\t\tnode: node,\n\t\tmin:  min,\n\t\tmax:  max,\n\t\tat:   min,\n\t}\n}\n\nfunc (it *int64Next) Close() error {\n\treturn nil\n}\n\nfunc (it *int64Next) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *int64Next) String() string {\n\treturn fmt.Sprintf(\"Int64(%d-%d)\", it.min, it.max)\n}\n\n// Next() on an Int64 all iterator is a simple incrementing counter.\n// Return the next integer, and mark it as the result.\nfunc (it *int64Next) Next(ctx context.Context) bool {\n\tif it.at == -1 {\n\t\treturn false\n\t}\n\tval := it.at\n\tit.at = it.at + 1\n\tif it.at > it.max {\n\t\tit.at = -1\n\t}\n\tit.result = val\n\treturn true\n}\n\nfunc (it *int64Next) Err() error {\n\treturn nil\n}\n\nfunc (it *int64Next) toValue(v int64) refs.Ref {\n\tif it.node {\n\t\treturn Int64Node(v)\n\t}\n\treturn Int64Quad(v)\n}\n\nfunc (it *int64Next) Result() refs.Ref {\n\treturn it.toValue(it.result)\n}\n\nfunc (it *int64Next) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// An All iterator across a range of int64 values, from `max` to `min`.\ntype int64Contains struct {\n\tnode     bool\n\tmax, min int64\n\tat       int64\n\tresult   int64\n}\n\n// Creates a new Int64 with the given range.\nfunc newInt64Contains(min, max int64, node bool) *int64Contains {\n\treturn &int64Contains{\n\t\tnode: node,\n\t\tmin:  min,\n\t\tmax:  max,\n\t\tat:   min,\n\t}\n}\n\nfunc (it *int64Contains) Close() error {\n\treturn nil\n}\n\nfunc (it *int64Contains) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *int64Contains) String() string {\n\treturn fmt.Sprintf(\"Int64(%d-%d)\", it.min, it.max)\n}\n\nfunc (it *int64Contains) Err() error {\n\treturn nil\n}\n\nfunc (it *int64Contains) toValue(v int64) refs.Ref {\n\tif it.node {\n\t\treturn Int64Node(v)\n\t}\n\treturn Int64Quad(v)\n}\n\nfunc (it *int64Contains) Result() refs.Ref {\n\treturn it.toValue(it.result)\n}\n\nfunc (it *int64Contains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// No sub-iterators.\nfunc (it *int64Contains) SubIterators() []Shape {\n\treturn nil\n}\n\n// Contains() for an Int64 is merely seeing if the passed value is\n// within the range, assuming the value is an int64.\nfunc (it *int64Contains) Contains(ctx context.Context, tsv refs.Ref) bool {\n\tv := valToInt64(tsv)\n\tif it.min <= v && v <= it.max {\n\t\tit.result = v\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "graph/iterator/limit.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Limit iterator will stop iterating if certain a number of values were encountered.\n// Zero and negative Limit values means no Limit.\ntype Limit struct {\n\tlimit int64\n\tit    Shape\n}\n\nfunc NewLimit(it Shape, max int64) *Limit {\n\treturn &Limit{\n\t\tlimit: max,\n\t\tit:    it,\n\t}\n}\n\nfunc (it *Limit) Iterate() Scanner {\n\treturn NewLimitNext(it.it.Iterate(), it.limit)\n}\n\nfunc (it *Limit) Lookup() Index {\n\treturn newLimitContains(it.it.Lookup(), it.limit)\n}\n\n// SubIterators returns a slice of the sub iterators.\nfunc (it *Limit) SubIterators() []Shape {\n\treturn []Shape{it.it}\n}\n\nfunc (it *Limit) Optimize(ctx context.Context) (Shape, bool) {\n\tnit, optimized := it.it.Optimize(ctx)\n\tif it.limit <= 0 { // no Limit\n\t\treturn nit, true\n\t}\n\tit.it = nit\n\treturn it, optimized\n}\n\nfunc (it *Limit) Stats(ctx context.Context) (Costs, error) {\n\tst, err := it.it.Stats(ctx)\n\tif it.limit > 0 && st.Size.Value > it.limit {\n\t\tst.Size.Value = it.limit\n\t}\n\treturn st, err\n}\n\nfunc (it *Limit) String() string {\n\treturn fmt.Sprintf(\"Limit(%d)\", it.limit)\n}\n\n// Limit iterator will stop iterating if certain a number of values were encountered.\n// Zero and negative Limit values means no Limit.\ntype limitNext struct {\n\tlimit int64\n\tcount int64\n\tit    Scanner\n}\n\nfunc NewLimitNext(it Scanner, limit int64) Scanner {\n\treturn &limitNext{\n\t\tlimit: limit,\n\t\tit:    it,\n\t}\n}\n\nfunc (it *limitNext) TagResults(dst map[string]refs.Ref) {\n\tit.it.TagResults(dst)\n}\n\n// Next advances the Limit iterator. It will stop iteration if Limit was reached.\nfunc (it *limitNext) Next(ctx context.Context) bool {\n\tif it.limit > 0 && it.count >= it.limit {\n\t\treturn false\n\t}\n\tif it.it.Next(ctx) {\n\t\tit.count++\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *limitNext) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *limitNext) Result() refs.Ref {\n\treturn it.it.Result()\n}\n\n// NextPath checks whether there is another path. Will call primary iterator\n// if Limit is not reached yet.\nfunc (it *limitNext) NextPath(ctx context.Context) bool {\n\tif it.limit > 0 && it.count >= it.limit {\n\t\treturn false\n\t}\n\tif it.it.NextPath(ctx) {\n\t\tit.count++\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *limitNext) Close() error {\n\treturn it.it.Close()\n}\n\nfunc (it *limitNext) String() string {\n\treturn fmt.Sprintf(\"LimitNext(%d)\", it.limit)\n}\n\n// Limit iterator will stop iterating if certain a number of values were encountered.\n// Zero and negative Limit values means no Limit.\ntype limitContains struct {\n\tlimit int64\n\tcount int64\n\tit    Index\n}\n\nfunc newLimitContains(it Index, limit int64) *limitContains {\n\treturn &limitContains{\n\t\tlimit: limit,\n\t\tit:    it,\n\t}\n}\n\nfunc (it *limitContains) TagResults(dst map[string]refs.Ref) {\n\tit.it.TagResults(dst)\n}\n\nfunc (it *limitContains) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *limitContains) Result() refs.Ref {\n\treturn it.it.Result()\n}\n\nfunc (it *limitContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tif it.limit > 0 && it.count >= it.limit {\n\t\treturn false\n\t}\n\tif it.it.Contains(ctx, val) {\n\t\tit.count++\n\t\treturn true\n\t}\n\treturn false\n}\n\n// NextPath checks whether there is another path. Will call primary iterator\n// if Limit is not reached yet.\nfunc (it *limitContains) NextPath(ctx context.Context) bool {\n\tif it.limit > 0 && it.count >= it.limit {\n\t\treturn false\n\t}\n\tif it.it.NextPath(ctx) {\n\t\tit.count++\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *limitContains) Close() error {\n\treturn it.it.Close()\n}\n\nfunc (it *limitContains) String() string {\n\treturn fmt.Sprintf(\"LimitContains(%d)\", it.limit)\n}\n"
  },
  {
    "path": "graph/iterator/limit_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nfunc TestLimitIteratorBasics(t *testing.T) {\n\tctx := context.TODO()\n\tallIt := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t\tInt64Node(5),\n\t)\n\n\tu := NewLimit(allIt, 0)\n\texpectSz, _ := allIt.Stats(ctx)\n\tsz, _ := u.Stats(ctx)\n\trequire.Equal(t, expectSz.Size.Value, sz.Size.Value)\n\trequire.Equal(t, []int{1, 2, 3, 4, 5}, iterated(u))\n\n\tu = NewLimit(allIt, 3)\n\tsz, _ = u.Stats(ctx)\n\trequire.Equal(t, int64(3), sz.Size.Value)\n\trequire.Equal(t, []int{1, 2, 3}, iterated(u))\n\n\tuc := u.Lookup()\n\tfor _, v := range []int{1, 2, 3} {\n\t\trequire.True(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\trequire.False(t, uc.Contains(ctx, Int64Node(4)))\n\n\tuc = u.Lookup()\n\tfor _, v := range []int{5, 4, 3} {\n\t\trequire.True(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\trequire.False(t, uc.Contains(ctx, Int64Node(2)))\n}\n"
  },
  {
    "path": "graph/iterator/materialize.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// A simple iterator that, when first called Contains() or Next() upon, materializes the whole subiterator, stores it locally, and responds. Essentially a cache.\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nconst MaterializeLimit = 1000\n\ntype result struct {\n\tid   refs.Ref\n\ttags map[string]refs.Ref\n}\n\ntype Materialize struct {\n\tsub        Shape\n\texpectSize int64\n}\n\nfunc NewMaterialize(sub Shape) *Materialize {\n\treturn NewMaterializeWithSize(sub, 0)\n}\n\nfunc NewMaterializeWithSize(sub Shape, size int64) *Materialize {\n\treturn &Materialize{\n\t\tsub:        sub,\n\t\texpectSize: size,\n\t}\n}\n\nfunc (it *Materialize) Iterate() Scanner {\n\treturn newMaterializeNext(it.sub)\n}\n\nfunc (it *Materialize) Lookup() Index {\n\treturn newMaterializeContains(it.sub)\n}\n\nfunc (it *Materialize) String() string {\n\treturn \"Materialize\"\n}\n\nfunc (it *Materialize) SubIterators() []Shape {\n\treturn []Shape{it.sub}\n}\n\nfunc (it *Materialize) Optimize(ctx context.Context) (Shape, bool) {\n\tnewSub, changed := it.sub.Optimize(ctx)\n\tif changed {\n\t\tit.sub = newSub\n\t\tif IsNull(it.sub) {\n\t\t\treturn it.sub, true\n\t\t}\n\t}\n\treturn it, false\n}\n\n// The entire point of Materialize is to amortize the cost by\n// putting it all up front.\nfunc (it *Materialize) Stats(ctx context.Context) (Costs, error) {\n\toverhead := int64(2)\n\tvar size refs.Size\n\tsubitStats, err := it.sub.Stats(ctx)\n\tif it.expectSize > 0 {\n\t\tsize = refs.Size{Value: it.expectSize, Exact: false}\n\t} else {\n\t\tsize = subitStats.Size\n\t}\n\treturn Costs{\n\t\tContainsCost: overhead * subitStats.NextCost,\n\t\tNextCost:     overhead * subitStats.NextCost,\n\t\tSize:         size,\n\t}, err\n}\n\ntype materializeNext struct {\n\tsub  Shape\n\tnext Scanner\n\n\tcontainsMap map[interface{}]int\n\tvalues      [][]result\n\tindex       int\n\tsubindex    int\n\thasRun      bool\n\taborted     bool\n\terr         error\n}\n\nfunc newMaterializeNext(sub Shape) *materializeNext {\n\treturn &materializeNext{\n\t\tcontainsMap: make(map[interface{}]int),\n\t\tsub:         sub,\n\t\tnext:        sub.Iterate(),\n\t\tindex:       -1,\n\t}\n}\n\nfunc (it *materializeNext) Close() error {\n\tit.containsMap = nil\n\tit.values = nil\n\tit.hasRun = false\n\treturn it.next.Close()\n}\n\nfunc (it *materializeNext) TagResults(dst map[string]refs.Ref) {\n\tif !it.hasRun {\n\t\treturn\n\t}\n\tif it.aborted {\n\t\tit.next.TagResults(dst)\n\t\treturn\n\t}\n\tif it.Result() == nil {\n\t\treturn\n\t}\n\tfor tag, value := range it.values[it.index][it.subindex].tags {\n\t\tdst[tag] = value\n\t}\n}\n\nfunc (it *materializeNext) String() string {\n\treturn \"Materialize\"\n}\n\nfunc (it *materializeNext) Result() refs.Ref {\n\tif it.aborted {\n\t\treturn it.next.Result()\n\t}\n\tif len(it.values) == 0 {\n\t\treturn nil\n\t}\n\tif it.index == -1 {\n\t\treturn nil\n\t}\n\tif it.index >= len(it.values) {\n\t\treturn nil\n\t}\n\treturn it.values[it.index][it.subindex].id\n}\n\nfunc (it *materializeNext) Next(ctx context.Context) bool {\n\tif !it.hasRun {\n\t\tit.materializeSet(ctx)\n\t}\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif it.aborted {\n\t\tn := it.next.Next(ctx)\n\t\tit.err = it.next.Err()\n\t\treturn n\n\t}\n\n\tit.index++\n\tit.subindex = 0\n\tif it.index >= len(it.values) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (it *materializeNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *materializeNext) NextPath(ctx context.Context) bool {\n\tif !it.hasRun {\n\t\tit.materializeSet(ctx)\n\t}\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif it.aborted {\n\t\treturn it.next.NextPath(ctx)\n\t}\n\n\tit.subindex++\n\tif it.subindex >= len(it.values[it.index]) {\n\t\t// Don't go off the end of the world\n\t\tit.subindex--\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (it *materializeNext) materializeSet(ctx context.Context) {\n\ti := 0\n\tmn := 0\n\tfor it.next.Next(ctx) {\n\t\ti++\n\t\tif i > MaterializeLimit {\n\t\t\tit.aborted = true\n\t\t\tbreak\n\t\t}\n\t\tid := it.next.Result()\n\t\tval := refs.ToKey(id)\n\t\tif _, ok := it.containsMap[val]; !ok {\n\t\t\tit.containsMap[val] = len(it.values)\n\t\t\tit.values = append(it.values, nil)\n\t\t}\n\t\tindex := it.containsMap[val]\n\t\ttags := make(map[string]refs.Ref, mn)\n\t\tit.next.TagResults(tags)\n\t\tif n := len(tags); n > mn {\n\t\t\tmn = n\n\t\t}\n\t\tit.values[index] = append(it.values[index], result{id: id, tags: tags})\n\t\tfor it.next.NextPath(ctx) {\n\t\t\ti++\n\t\t\tif i > MaterializeLimit {\n\t\t\t\tit.aborted = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttags := make(map[string]refs.Ref, mn)\n\t\t\tit.next.TagResults(tags)\n\t\t\tif n := len(tags); n > mn {\n\t\t\t\tmn = n\n\t\t\t}\n\t\t\tit.values[index] = append(it.values[index], result{id: id, tags: tags})\n\t\t}\n\t}\n\tit.err = it.next.Err()\n\tif it.err == nil && it.aborted {\n\t\tif clog.V(2) {\n\t\t\tclog.Infof(\"Aborting subiterator\")\n\t\t}\n\t\tit.values = nil\n\t\tit.containsMap = nil\n\t\t_ = it.next.Close()\n\t\tit.next = it.sub.Iterate()\n\t}\n\tit.hasRun = true\n}\n\ntype materializeContains struct {\n\tnext *materializeNext\n\tsub  Index // only set if aborted\n}\n\nfunc newMaterializeContains(sub Shape) *materializeContains {\n\treturn &materializeContains{\n\t\tnext: newMaterializeNext(sub),\n\t}\n}\n\nfunc (it *materializeContains) Close() error {\n\terr := it.next.Close()\n\tif it.sub != nil {\n\t\tif err2 := it.sub.Close(); err2 != nil && err == nil {\n\t\t\terr = err2\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (it *materializeContains) TagResults(dst map[string]refs.Ref) {\n\tif it.sub != nil {\n\t\tit.sub.TagResults(dst)\n\t\treturn\n\t}\n\tit.next.TagResults(dst)\n}\n\nfunc (it *materializeContains) String() string {\n\treturn \"MaterializeContains\"\n}\n\nfunc (it *materializeContains) Result() refs.Ref {\n\tif it.sub != nil {\n\t\treturn it.sub.Result()\n\t}\n\treturn it.next.Result()\n}\n\nfunc (it *materializeContains) Err() error {\n\tif err := it.next.Err(); err != nil {\n\t\treturn err\n\t} else if it.sub == nil {\n\t\treturn nil\n\t}\n\treturn it.sub.Err()\n}\n\nfunc (it *materializeContains) run(ctx context.Context) {\n\tit.next.materializeSet(ctx)\n\tif it.next.aborted {\n\t\tit.sub = it.next.sub.Lookup()\n\t}\n}\n\nfunc (it *materializeContains) Contains(ctx context.Context, v refs.Ref) bool {\n\tif !it.next.hasRun {\n\t\tit.run(ctx)\n\t}\n\tif it.next.Err() != nil {\n\t\treturn false\n\t}\n\tif it.sub != nil {\n\t\treturn it.sub.Contains(ctx, v)\n\t}\n\tkey := refs.ToKey(v)\n\tif i, ok := it.next.containsMap[key]; ok {\n\t\tit.next.index = i\n\t\tit.next.subindex = 0\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *materializeContains) NextPath(ctx context.Context) bool {\n\tif !it.next.hasRun {\n\t\tit.run(ctx)\n\t}\n\tif it.next.Err() != nil {\n\t\treturn false\n\t}\n\tif it.sub != nil {\n\t\treturn it.sub.NextPath(ctx)\n\t}\n\treturn it.next.NextPath(ctx)\n}\n"
  },
  {
    "path": "graph/iterator/materialize_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nfunc TestMaterializeIteratorError(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\terrIt := newTestIterator(false, wantErr)\n\n\t// This tests that we properly return 0 results and the error when the\n\t// underlying iterator returns an error.\n\tmIt := NewMaterialize(errIt).Iterate()\n\n\trequire.False(t, mIt.Next(ctx))\n\trequire.Equal(t, wantErr, mIt.Err())\n}\n\nfunc TestMaterializeIteratorErrorAbort(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\terrIt := newTestIterator(false, wantErr)\n\n\t// This tests that we properly return 0 results and the error when the\n\t// underlying iterator is larger than our 'abort at' value, and then\n\t// returns an error.\n\tor := NewOr(\n\t\tnewInt64(1, int64(MaterializeLimit+1), true),\n\t\terrIt,\n\t)\n\n\tmIt := NewMaterialize(or).Iterate()\n\n\t// We should get all the underlying values...\n\tfor i := 0; i < MaterializeLimit+1; i++ {\n\t\trequire.True(t, mIt.Next(ctx))\n\t\trequire.NoError(t, mIt.Err())\n\t}\n\n\t// ... and then the error value.\n\trequire.False(t, mIt.Next(ctx))\n\trequire.Equal(t, wantErr, mIt.Err())\n}\n"
  },
  {
    "path": "graph/iterator/misc.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// Defines one of the base iterators, the All iterator. Which, logically\n// enough, represents all nodes or all links in the graph.\n//\n// This particular file is actually vestigial. It's up to the QuadStore to give\n// us an All iterator that represents all things in the graph. So this is\n// really the All iterator for the memstore.QuadStore. That said, it *is* one of\n// the base iterators, and it helps just to see it here.\n\ntype Int64Node int64\n\nfunc (v Int64Node) Key() interface{} { return v }\n\nfunc (Int64Node) IsNode() bool { return true }\n"
  },
  {
    "path": "graph/iterator/not.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Not iterator acts like a complement for the primary iterator.\n// It will return all the vertices which are not part of the primary iterator.\ntype Not struct {\n\tprimary Shape\n\tallIt   Shape\n}\n\nfunc NewNot(primaryIt, allIt Shape) *Not {\n\treturn &Not{\n\t\tprimary: primaryIt,\n\t\tallIt:   allIt,\n\t}\n}\n\nfunc (it *Not) Iterate() Scanner {\n\treturn newNotNext(it.primary.Lookup(), it.allIt.Iterate())\n}\n\nfunc (it *Not) Lookup() Index {\n\treturn newNotContains(it.primary.Lookup())\n}\n\n// SubIterators returns a slice of the sub iterators.\n// The first iterator is the primary iterator, for which the complement\n// is generated.\nfunc (it *Not) SubIterators() []Shape {\n\treturn []Shape{it.primary, it.allIt}\n}\n\nfunc (it *Not) Optimize(ctx context.Context) (Shape, bool) {\n\t// TODO - consider wrapping the primary with a MaterializeIt\n\toptimizedPrimaryIt, optimized := it.primary.Optimize(ctx)\n\tif optimized {\n\t\tit.primary = optimizedPrimaryIt\n\t}\n\tit.primary = NewMaterialize(it.primary)\n\treturn it, false\n}\n\nfunc (it *Not) Stats(ctx context.Context) (Costs, error) {\n\tprimaryStats, err := it.primary.Stats(ctx)\n\tallStats, err2 := it.allIt.Stats(ctx)\n\tif err == nil {\n\t\terr = err2\n\t}\n\treturn Costs{\n\t\tNextCost:     allStats.NextCost + primaryStats.ContainsCost,\n\t\tContainsCost: primaryStats.ContainsCost,\n\t\tSize: refs.Size{\n\t\t\tValue: allStats.Size.Value - primaryStats.Size.Value,\n\t\t\tExact: false,\n\t\t},\n\t}, err\n}\n\nfunc (it *Not) String() string {\n\treturn \"Not\"\n}\n\n// Not iterator acts like a complement for the primary iterator.\n// It will return all the vertices which are not part of the primary iterator.\ntype notNext struct {\n\tprimaryIt Index\n\tallIt     Scanner\n\tresult    refs.Ref\n}\n\nfunc newNotNext(primaryIt Index, allIt Scanner) *notNext {\n\treturn &notNext{\n\t\tprimaryIt: primaryIt,\n\t\tallIt:     allIt,\n\t}\n}\n\nfunc (it *notNext) TagResults(dst map[string]refs.Ref) {\n\tif it.primaryIt != nil {\n\t\tit.primaryIt.TagResults(dst)\n\t}\n}\n\n// Next advances the Not iterator. It returns whether there is another valid\n// new value. It fetches the next value of the all iterator which is not\n// contained by the primary iterator.\nfunc (it *notNext) Next(ctx context.Context) bool {\n\tfor it.allIt.Next(ctx) {\n\t\tif curr := it.allIt.Result(); !it.primaryIt.Contains(ctx, curr) {\n\t\t\tit.result = curr\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (it *notNext) Err() error {\n\tif err := it.allIt.Err(); err != nil {\n\t\treturn err\n\t}\n\tif err := it.primaryIt.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (it *notNext) Result() refs.Ref {\n\treturn it.result\n}\n\n// NextPath checks whether there is another path. Not applicable, hence it will\n// return false.\nfunc (it *notNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *notNext) Close() error {\n\terr := it.primaryIt.Close()\n\tif err2 := it.allIt.Close(); err2 != nil && err == nil {\n\t\terr = err2\n\t}\n\treturn err\n}\n\nfunc (it *notNext) String() string {\n\treturn \"NotNext\"\n}\n\n// Not iterator acts like a complement for the primary iterator.\n// It will return all the vertices which are not part of the primary iterator.\ntype notContains struct {\n\tprimaryIt Index\n\tresult    refs.Ref\n\terr       error\n}\n\nfunc newNotContains(primaryIt Index) *notContains {\n\treturn &notContains{\n\t\tprimaryIt: primaryIt,\n\t}\n}\n\nfunc (it *notContains) TagResults(dst map[string]refs.Ref) {\n\tif it.primaryIt != nil {\n\t\tit.primaryIt.TagResults(dst)\n\t}\n}\n\nfunc (it *notContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *notContains) Result() refs.Ref {\n\treturn it.result\n}\n\n// Contains checks whether the passed value is part of the primary iterator's\n// complement. For a valid value, it updates the Result returned by the iterator\n// to the value itself.\nfunc (it *notContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tif it.primaryIt.Contains(ctx, val) {\n\t\treturn false\n\t}\n\tit.err = it.primaryIt.Err()\n\tif it.err != nil {\n\t\t// Explicitly return 'false', since an error occurred.\n\t\treturn false\n\t}\n\tit.result = val\n\treturn true\n}\n\n// NextPath checks whether there is another path. Not applicable, hence it will\n// return false.\nfunc (it *notContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *notContains) Close() error {\n\treturn it.primaryIt.Close()\n}\n\nfunc (it *notContains) String() string {\n\treturn \"NotContains\"\n}\n"
  },
  {
    "path": "graph/iterator/not_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nfunc TestNotIteratorBasics(t *testing.T) {\n\tctx := context.TODO()\n\tallIt := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t)\n\n\ttoComplementIt := NewFixed(\n\t\tInt64Node(2),\n\t\tInt64Node(4),\n\t)\n\n\tnot := NewNot(toComplementIt, allIt)\n\n\tst, _ := not.Stats(ctx)\n\trequire.Equal(t, int64(2), st.Size.Value)\n\n\texpect := []int{1, 3}\n\tfor i := 0; i < 2; i++ {\n\t\trequire.Equal(t, expect, iterated(not))\n\t}\n\n\tnc := not.Lookup()\n\tfor _, v := range []int{1, 3} {\n\t\trequire.True(t, nc.Contains(ctx, Int64Node(v)))\n\t}\n\n\tfor _, v := range []int{2, 4} {\n\t\trequire.False(t, nc.Contains(ctx, Int64Node(v)))\n\t}\n}\n\nfunc TestNotIteratorErr(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\tallIt := newTestIterator(false, wantErr)\n\n\ttoComplementIt := NewFixed()\n\n\tnot := NewNot(toComplementIt, allIt).Iterate()\n\n\trequire.False(t, not.Next(ctx))\n\trequire.Equal(t, wantErr, not.Err())\n}\n"
  },
  {
    "path": "graph/iterator/or.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// Defines the or and short-circuiting or iterator. Or is the union operator for it's subiterators.\n// Short-circuiting-or is a little different. It will return values from the first iterator that returns\n// values at all, and then stops.\n//\n// Never reorders the iterators from the order they arrive. It is either the union or the first one.\n// May return the same value twice -- once for each branch.\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\ntype Or struct {\n\tisShortCircuiting bool\n\tsub               []Shape\n\tcurInd            int\n\tresult            refs.Ref\n\terr               error\n}\n\nfunc NewOr(sub ...Shape) *Or {\n\tit := &Or{\n\t\tsub:    make([]Shape, 0, 20),\n\t\tcurInd: -1,\n\t}\n\tfor _, s := range sub {\n\t\tit.AddSubIterator(s)\n\t}\n\treturn it\n}\n\nfunc NewShortCircuitOr(sub ...Shape) *Or {\n\tit := &Or{\n\t\tsub:               make([]Shape, 0, 20),\n\t\tisShortCircuiting: true,\n\t\tcurInd:            -1,\n\t}\n\tfor _, s := range sub {\n\t\tit.AddSubIterator(s)\n\t}\n\treturn it\n}\n\nfunc (it *Or) Iterate() Scanner {\n\tsub := make([]Scanner, 0, len(it.sub))\n\tfor _, s := range it.sub {\n\t\tsub = append(sub, s.Iterate())\n\t}\n\treturn newOrNext(sub, it.isShortCircuiting)\n}\n\nfunc (it *Or) Lookup() Index {\n\tsub := make([]Index, 0, len(it.sub))\n\tfor _, s := range it.sub {\n\t\tsub = append(sub, s.Lookup())\n\t}\n\treturn newOrContains(sub, it.isShortCircuiting)\n}\n\n// Returns a list.List of the subiterators, in order. The returned slice must not be modified.\nfunc (it *Or) SubIterators() []Shape {\n\treturn it.sub\n}\n\nfunc (it *Or) String() string {\n\treturn \"Or\"\n}\n\n// Add a subiterator to this Or iterator. Order matters.\nfunc (it *Or) AddSubIterator(sub Shape) {\n\tit.sub = append(it.sub, sub)\n}\n\nfunc (it *Or) Optimize(ctx context.Context) (Shape, bool) {\n\told := it.SubIterators()\n\toptIts := optimizeSubIterators(ctx, old)\n\tnewOr := NewOr()\n\tnewOr.isShortCircuiting = it.isShortCircuiting\n\n\t// Add the subiterators in order.\n\tfor _, o := range optIts {\n\t\tnewOr.AddSubIterator(o)\n\t}\n\treturn newOr, true\n}\n\n// Returns the approximate size of the Or iterator. Because we're dealing\n// with a union, we know that the largest we can be is the sum of all the iterators,\n// or in the case of short-circuiting, the longest.\nfunc (it *Or) Stats(ctx context.Context) (Costs, error) {\n\tContainsCost := int64(0)\n\tNextCost := int64(0)\n\tSize := refs.Size{\n\t\tValue: 0,\n\t\tExact: true,\n\t}\n\tvar last error\n\tfor _, sub := range it.sub {\n\t\tstats, err := sub.Stats(ctx)\n\t\tif err != nil {\n\t\t\tlast = err\n\t\t}\n\t\tNextCost += stats.NextCost\n\t\tContainsCost += stats.ContainsCost\n\t\tif it.isShortCircuiting {\n\t\t\tif Size.Value < stats.Size.Value {\n\t\t\t\tSize = stats.Size\n\t\t\t}\n\t\t} else {\n\t\t\tSize.Value += stats.Size.Value\n\t\t\tSize.Exact = Size.Exact && stats.Size.Exact\n\t\t}\n\t}\n\treturn Costs{\n\t\tContainsCost: ContainsCost,\n\t\tNextCost:     NextCost,\n\t\tSize:         Size,\n\t}, last\n}\n\ntype orNext struct {\n\tshortCircuit bool\n\tsub          []Scanner\n\tcurInd       int\n\tresult       refs.Ref\n\terr          error\n}\n\nfunc newOrNext(sub []Scanner, shortCircuit bool) *orNext {\n\treturn &orNext{\n\t\tsub:          sub,\n\t\tcurInd:       -1,\n\t\tshortCircuit: shortCircuit,\n\t}\n}\n\n// Overrides BaseIterator TagResults, as it needs to add it's own results and\n// recurse down it's subiterators.\nfunc (it *orNext) TagResults(dst map[string]refs.Ref) {\n\tit.sub[it.curInd].TagResults(dst)\n}\n\nfunc (it *orNext) String() string {\n\treturn \"OrNext\"\n}\n\n// Next advances the Or iterator. Because the Or is the union of its\n// subiterators, it must produce from all subiterators -- unless it it\n// shortcircuiting, in which case, it is the first one that returns anything.\nfunc (it *orNext) Next(ctx context.Context) bool {\n\tif it.curInd >= len(it.sub) {\n\t\treturn false\n\t}\n\tvar first bool\n\tfor {\n\t\tif it.curInd == -1 {\n\t\t\tit.curInd = 0\n\t\t\tfirst = true\n\t\t}\n\t\tcurIt := it.sub[it.curInd]\n\n\t\tif curIt.Next(ctx) {\n\t\t\tit.result = curIt.Result()\n\t\t\treturn true\n\t\t}\n\n\t\tit.err = curIt.Err()\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif it.shortCircuit && !first {\n\t\t\tbreak\n\t\t}\n\t\tit.curInd++\n\t\tif it.curInd >= len(it.sub) {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (it *orNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *orNext) Result() refs.Ref {\n\treturn it.result\n}\n\n// An Or has no NextPath of its own -- that is, there are no other values\n// which satisfy our previous result that are not the result itself. Our\n// subiterators might, however, so just pass the call recursively. In the case of\n// shortcircuiting, only allow new results from the currently checked iterator\nfunc (it *orNext) NextPath(ctx context.Context) bool {\n\tif it.curInd != -1 {\n\t\tcurrIt := it.sub[it.curInd]\n\t\tok := currIt.NextPath(ctx)\n\t\tif !ok {\n\t\t\tit.err = currIt.Err()\n\t\t}\n\t\treturn ok\n\t}\n\treturn false\n}\n\n// Close this iterator, and, by extension, close the subiterators.\n// Close should be idempotent, and it follows that if it's subiterators\n// follow this contract, the Or follows the contract.  It closes all\n// subiterators it can, but returns the first error it encounters.\nfunc (it *orNext) Close() error {\n\tvar err error\n\tfor _, sub := range it.sub {\n\t\t_err := sub.Close()\n\t\tif _err != nil && err == nil {\n\t\t\terr = _err\n\t\t}\n\t}\n\treturn err\n}\n\ntype orContains struct {\n\tshortCircuit bool\n\tsub          []Index\n\tcurInd       int\n\tresult       refs.Ref\n\terr          error\n}\n\nfunc newOrContains(sub []Index, shortCircuit bool) *orContains {\n\treturn &orContains{\n\t\tsub:          sub,\n\t\tcurInd:       -1,\n\t\tshortCircuit: shortCircuit,\n\t}\n}\n\n// Overrides BaseIterator TagResults, as it needs to add it's own results and\n// recurse down it's subiterators.\nfunc (it *orContains) TagResults(dst map[string]refs.Ref) {\n\tit.sub[it.curInd].TagResults(dst)\n}\n\nfunc (it *orContains) String() string {\n\treturn \"OrContains\"\n}\n\nfunc (it *orContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *orContains) Result() refs.Ref {\n\treturn it.result\n}\n\n// Checks a value against the iterators, in order.\nfunc (it *orContains) subItsContain(ctx context.Context, val refs.Ref) (bool, error) {\n\tsubIsGood := false\n\tfor i, sub := range it.sub {\n\t\tsubIsGood = sub.Contains(ctx, val)\n\t\tif subIsGood {\n\t\t\tit.curInd = i\n\t\t\tbreak\n\t\t}\n\n\t\terr := sub.Err()\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\treturn subIsGood, nil\n}\n\n// Check a value against the entire iterator, in order.\nfunc (it *orContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tanyGood, err := it.subItsContain(ctx, val)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t} else if !anyGood {\n\t\treturn false\n\t}\n\tit.result = val\n\treturn true\n}\n\n// An Or has no NextPath of its own -- that is, there are no other values\n// which satisfy our previous result that are not the result itself. Our\n// subiterators might, however, so just pass the call recursively. In the case of\n// shortcircuiting, only allow new results from the currently checked iterator\nfunc (it *orContains) NextPath(ctx context.Context) bool {\n\tif it.curInd != -1 {\n\t\tcurrIt := it.sub[it.curInd]\n\t\tok := currIt.NextPath(ctx)\n\t\tif !ok {\n\t\t\tit.err = currIt.Err()\n\t\t}\n\t\treturn ok\n\t}\n\t// TODO(dennwc): this should probably list matches from other sub-iterators\n\treturn false\n}\n\n// Close this iterator, and, by extension, close the subiterators.\n// Close should be idempotent, and it follows that if it's subiterators\n// follow this contract, the Or follows the contract.  It closes all\n// subiterators it can, but returns the first error it encounters.\nfunc (it *orContains) Close() error {\n\tvar err error\n\tfor _, sub := range it.sub {\n\t\t_err := sub.Close()\n\t\tif _err != nil && err == nil {\n\t\t\terr = _err\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "graph/iterator/or_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nfunc iterated(s Shape) []int {\n\tctx := context.TODO()\n\tvar res []int\n\tit := s.Iterate()\n\tdefer it.Close()\n\tfor it.Next(ctx) {\n\t\tres = append(res, int(it.Result().(Int64Node)))\n\t}\n\treturn res\n}\n\nfunc TestOrIteratorBasics(t *testing.T) {\n\tctx := context.TODO()\n\tor := NewOr()\n\tf1 := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t)\n\tf2 := NewFixed(\n\t\tInt64Node(3),\n\t\tInt64Node(9),\n\t\tInt64Node(20),\n\t\tInt64Node(21),\n\t)\n\tor.AddSubIterator(f1)\n\tor.AddSubIterator(f2)\n\n\tst, _ := or.Stats(ctx)\n\trequire.Equal(t, int64(7), st.Size.Value)\n\n\texpect := []int{1, 2, 3, 3, 9, 20, 21}\n\tfor i := 0; i < 2; i++ {\n\t\trequire.Equal(t, expect, iterated(or))\n\t}\n\n\t// Check that optimization works.\n\toptOr, _ := or.Optimize(ctx)\n\trequire.Equal(t, expect, iterated(optOr))\n\n\torc := or.Lookup()\n\tfor _, v := range []int{2, 3, 21} {\n\t\trequire.True(t, orc.Contains(ctx, Int64Node(v)))\n\t}\n\n\tfor _, v := range []int{22, 5, 0} {\n\t\trequire.False(t, orc.Contains(ctx, Int64Node(v)))\n\t}\n}\n\nfunc TestShortCircuitingOrBasics(t *testing.T) {\n\tctx := context.TODO()\n\tvar or *Or\n\n\tf1 := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t)\n\tf2 := NewFixed(\n\t\tInt64Node(3),\n\t\tInt64Node(9),\n\t\tInt64Node(20),\n\t\tInt64Node(21),\n\t)\n\n\tor = NewShortCircuitOr()\n\tor.AddSubIterator(f1)\n\tor.AddSubIterator(f2)\n\tst, _ := or.Stats(ctx)\n\trequire.Equal(t, refs.Size{\n\t\tValue: 4,\n\t\tExact: true,\n\t}, st.Size)\n\n\t// It should extract the first iterators' numbers.\n\tor = NewShortCircuitOr()\n\tor.AddSubIterator(f1)\n\tor.AddSubIterator(f2)\n\texpect := []int{1, 2, 3}\n\tfor i := 0; i < 2; i++ {\n\t\trequire.Equal(t, expect, iterated(or))\n\t}\n\n\t// Check optimization works.\n\toptOr, _ := or.Optimize(ctx)\n\trequire.Equal(t, expect, iterated(optOr))\n\n\t// Check that numbers in either iterator exist.\n\tor = NewShortCircuitOr()\n\tor.AddSubIterator(f1)\n\tor.AddSubIterator(f2)\n\n\torc := or.Lookup()\n\tfor _, v := range []int{2, 3, 21} {\n\t\trequire.True(t, orc.Contains(ctx, Int64Node(v)))\n\t}\n\tfor _, v := range []int{22, 5, 0} {\n\t\trequire.False(t, orc.Contains(ctx, Int64Node(v)))\n\t}\n\n\t// Check that it pulls the second iterator's numbers if the first is empty.\n\tor = NewShortCircuitOr()\n\tor.AddSubIterator(NewFixed())\n\tor.AddSubIterator(f2)\n\n\texpect = []int{3, 9, 20, 21}\n\tfor i := 0; i < 2; i++ {\n\t\trequire.Equal(t, expect, iterated(or))\n\t}\n\t// Check optimization works.\n\toptOr, _ = or.Optimize(ctx)\n\trequire.Equal(t, expect, iterated(optOr))\n}\n\nfunc TestOrIteratorErr(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\torErr := newTestIterator(false, wantErr)\n\n\tfix1 := NewFixed(Int64Node(1))\n\n\tor := NewOr(\n\t\tfix1,\n\t\torErr,\n\t\tnewInt64(1, 5, true),\n\t).Iterate()\n\n\trequire.True(t, or.Next(ctx))\n\trequire.Equal(t, Int64Node(1), or.Result())\n\n\trequire.False(t, or.Next(ctx))\n\trequire.Equal(t, wantErr, or.Err())\n}\n\nfunc TestShortCircuitOrIteratorErr(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\torErr := newTestIterator(false, wantErr)\n\n\tor := NewOr(\n\t\torErr,\n\t\tnewInt64(1, 5, true),\n\t).Iterate()\n\n\trequire.False(t, or.Next(ctx))\n\trequire.Equal(t, wantErr, or.Err())\n}\n"
  },
  {
    "path": "graph/iterator/recursive.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"math\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nconst recursiveBaseTag = \"__base_recursive\"\n\ntype seenAt struct {\n\tdepth int\n\ttags  map[string]refs.Ref\n\tval   refs.Ref\n}\n\nvar DefaultMaxRecursiveSteps = 50\n\n// Recursive iterator takes a base iterator and a morphism to be applied recursively, for each result.\ntype Recursive struct {\n\tsubIt     Shape\n\tmorphism  Morphism\n\tmaxDepth  int\n\tdepthTags []string\n}\n\nfunc NewRecursive(it Shape, morphism Morphism, maxDepth int) *Recursive {\n\tif maxDepth == 0 {\n\t\tmaxDepth = DefaultMaxRecursiveSteps\n\t}\n\treturn &Recursive{\n\t\tsubIt:    it,\n\t\tmorphism: morphism,\n\t\tmaxDepth: maxDepth,\n\t}\n}\n\nfunc (it *Recursive) Iterate() Scanner {\n\treturn newRecursiveNext(it.subIt.Iterate(), it.morphism, it.maxDepth, it.depthTags)\n}\n\nfunc (it *Recursive) Lookup() Index {\n\treturn newRecursiveContains(newRecursiveNext(it.subIt.Iterate(), it.morphism, it.maxDepth, it.depthTags))\n}\n\nfunc (it *Recursive) AddDepthTag(s string) {\n\tit.depthTags = append(it.depthTags, s)\n}\n\nfunc (it *Recursive) SubIterators() []Shape {\n\treturn []Shape{it.subIt}\n}\n\nfunc (it *Recursive) Optimize(ctx context.Context) (Shape, bool) {\n\tnewIt, optimized := it.subIt.Optimize(ctx)\n\tif optimized {\n\t\tit.subIt = newIt\n\t}\n\treturn it, false\n}\n\nfunc (it *Recursive) Stats(ctx context.Context) (Costs, error) {\n\tbase := NewFixed()\n\tbase.Add(Int64Node(20))\n\tfanoutit := it.morphism(base)\n\tfanoutStats, err := fanoutit.Stats(ctx)\n\tsubitStats, err2 := it.subIt.Stats(ctx)\n\tif err == nil {\n\t\terr = err2\n\t}\n\tsize := int64(math.Pow(float64(subitStats.Size.Value*fanoutStats.Size.Value), 5))\n\treturn Costs{\n\t\tNextCost:     subitStats.NextCost + fanoutStats.NextCost,\n\t\tContainsCost: (subitStats.NextCost+fanoutStats.NextCost)*(size/10) + subitStats.ContainsCost,\n\t\tSize: refs.Size{\n\t\t\tValue: size,\n\t\t\tExact: false,\n\t\t},\n\t}, err\n}\n\nfunc (it *Recursive) String() string {\n\treturn \"Recursive\"\n}\n\n// Recursive iterator takes a base iterator and a morphism to be applied recursively, for each result.\ntype recursiveNext struct {\n\tsubIt  Scanner\n\tresult seenAt\n\terr    error\n\n\tmorphism      Morphism\n\tseen          map[interface{}]seenAt\n\tnextIt        Scanner\n\tdepth         int\n\tmaxDepth      int\n\tpathMap       map[interface{}][]map[string]refs.Ref\n\tpathIndex     int\n\tcontainsValue refs.Ref\n\tdepthTags     []string\n\tdepthCache    []refs.Ref\n\tbaseIt        *Fixed\n}\n\nfunc newRecursiveNext(it Scanner, morphism Morphism, maxDepth int, depthTags []string) *recursiveNext {\n\treturn &recursiveNext{\n\t\tsubIt:     it,\n\t\tmorphism:  morphism,\n\t\tmaxDepth:  maxDepth,\n\t\tdepthTags: depthTags,\n\n\t\tseen:    make(map[interface{}]seenAt),\n\t\tnextIt:  &Null{},\n\t\tbaseIt:  NewFixed(),\n\t\tpathMap: make(map[interface{}][]map[string]refs.Ref),\n\t}\n}\n\nfunc (it *recursiveNext) TagResults(dst map[string]refs.Ref) {\n\tfor _, tag := range it.depthTags {\n\t\tdst[tag] = refs.PreFetched(quad.Int(it.result.depth))\n\t}\n\n\tif it.containsValue != nil {\n\t\tpaths := it.pathMap[refs.ToKey(it.containsValue)]\n\t\tif len(paths) != 0 {\n\t\t\tfor k, v := range paths[it.pathIndex] {\n\t\t\t\tdst[k] = v\n\t\t\t}\n\t\t}\n\t}\n\tif it.nextIt != nil {\n\t\tit.nextIt.TagResults(dst)\n\t\tdelete(dst, recursiveBaseTag)\n\t}\n}\n\nfunc (it *recursiveNext) Next(ctx context.Context) bool {\n\tit.pathIndex = 0\n\tif it.depth == 0 {\n\t\tfor it.subIt.Next(ctx) {\n\t\t\tres := it.subIt.Result()\n\t\t\tit.depthCache = append(it.depthCache, it.subIt.Result())\n\t\t\ttags := make(map[string]refs.Ref)\n\t\t\tit.subIt.TagResults(tags)\n\t\t\tkey := refs.ToKey(res)\n\t\t\tit.pathMap[key] = append(it.pathMap[key], tags)\n\t\t\tfor it.subIt.NextPath(ctx) {\n\t\t\t\ttags := make(map[string]refs.Ref)\n\t\t\t\tit.subIt.TagResults(tags)\n\t\t\t\tit.pathMap[key] = append(it.pathMap[key], tags)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\tif !it.nextIt.Next(ctx) {\n\t\t\tif it.maxDepth > 0 && it.depth >= it.maxDepth {\n\t\t\t\treturn false\n\t\t\t} else if len(it.depthCache) == 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tit.depth++\n\t\t\tit.baseIt = NewFixed(it.depthCache...)\n\t\t\tit.depthCache = nil\n\t\t\tif it.nextIt != nil {\n\t\t\t\tit.nextIt.Close()\n\t\t\t}\n\t\t\tit.nextIt = it.morphism(Tag(it.baseIt, recursiveBaseTag)).Iterate()\n\t\t\tcontinue\n\t\t}\n\t\tval := it.nextIt.Result()\n\t\tresults := make(map[string]refs.Ref)\n\t\tit.nextIt.TagResults(results)\n\t\tkey := refs.ToKey(val)\n\t\tif _, seen := it.seen[key]; !seen {\n\t\t\tbase := results[recursiveBaseTag]\n\t\t\tdelete(results, recursiveBaseTag)\n\t\t\tit.seen[key] = seenAt{\n\t\t\t\tval:   base,\n\t\t\t\tdepth: it.depth,\n\t\t\t\ttags:  results,\n\t\t\t}\n\t\t\tit.result.depth = it.depth\n\t\t\tit.result.val = val\n\t\t\tit.containsValue = it.getBaseValue(val)\n\t\t\tit.depthCache = append(it.depthCache, val)\n\t\t\treturn true\n\t\t}\n\t}\n}\n\nfunc (it *recursiveNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *recursiveNext) Result() refs.Ref {\n\treturn it.result.val\n}\n\nfunc (it *recursiveNext) getBaseValue(val refs.Ref) refs.Ref {\n\tvar at seenAt\n\tvar ok bool\n\tif at, ok = it.seen[refs.ToKey(val)]; !ok {\n\t\tpanic(\"trying to getBaseValue of something unseen\")\n\t}\n\tfor at.depth != 1 {\n\t\tif at.depth == 0 {\n\t\t\tpanic(\"seen chain is broken\")\n\t\t}\n\t\tat = it.seen[refs.ToKey(at.val)]\n\t}\n\treturn at.val\n}\n\nfunc (it *recursiveNext) NextPath(ctx context.Context) bool {\n\tif it.pathIndex+1 >= len(it.pathMap[refs.ToKey(it.containsValue)]) {\n\t\treturn false\n\t}\n\tit.pathIndex++\n\treturn true\n}\n\nfunc (it *recursiveNext) Close() error {\n\terr := it.subIt.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = it.nextIt.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tit.seen = nil\n\treturn it.err\n}\n\nfunc (it *recursiveNext) String() string {\n\treturn \"RecursiveNext\"\n}\n\n// Recursive iterator takes a base iterator and a morphism to be applied recursively, for each result.\ntype recursiveContains struct {\n\tnext *recursiveNext\n\ttags map[string]refs.Ref\n}\n\nfunc newRecursiveContains(next *recursiveNext) *recursiveContains {\n\treturn &recursiveContains{\n\t\tnext: next,\n\t}\n}\n\nfunc (it *recursiveContains) TagResults(dst map[string]refs.Ref) {\n\tit.next.TagResults(dst)\n\tfor k, v := range it.tags {\n\t\tdst[k] = v\n\t}\n}\n\nfunc (it *recursiveContains) Err() error {\n\treturn it.next.Err()\n}\n\nfunc (it *recursiveContains) Result() refs.Ref {\n\treturn it.next.Result()\n}\n\nfunc (it *recursiveContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tit.next.pathIndex = 0\n\tkey := refs.ToKey(val)\n\tif at, ok := it.next.seen[key]; ok {\n\t\tit.next.containsValue = it.next.getBaseValue(val)\n\t\tit.next.result.depth = at.depth\n\t\tit.next.result.val = val\n\t\tit.tags = at.tags\n\t\treturn true\n\t}\n\tfor it.next.Next(ctx) {\n\t\tif refs.ToKey(it.next.Result()) == key {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (it *recursiveContains) NextPath(ctx context.Context) bool {\n\treturn it.next.NextPath(ctx)\n}\n\nfunc (it *recursiveContains) Close() error {\n\treturn it.next.Close()\n}\n\nfunc (it *recursiveContains) String() string {\n\treturn \"RecursiveContains(\" + it.next.String() + \")\"\n}\n"
  },
  {
    "path": "graph/iterator/recursive_test.go",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphmock\"\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc singleHop(qs graph.QuadIndexer, pred string) Morphism {\n\treturn func(it Shape) Shape {\n\t\tfixed := NewFixed()\n\t\tfixed.Add(refs.PreFetched(quad.Raw(pred)))\n\t\tpredlto := graph.NewLinksTo(qs, fixed, quad.Predicate)\n\t\tlto := graph.NewLinksTo(qs, it, quad.Subject)\n\t\tand := NewAnd()\n\t\tand.AddSubIterator(lto)\n\t\tand.AddSubIterator(predlto)\n\t\treturn graph.NewHasA(qs, and, quad.Object)\n\t}\n}\n\nvar recTestQs = &graphmock.Store{\n\tData: []quad.Quad{\n\t\tquad.MakeRaw(\"alice\", \"parent\", \"bob\", \"\"),\n\t\tquad.MakeRaw(\"bob\", \"parent\", \"charlie\", \"\"),\n\t\tquad.MakeRaw(\"charlie\", \"parent\", \"dani\", \"\"),\n\t\tquad.MakeRaw(\"charlie\", \"parent\", \"bob\", \"\"),\n\t\tquad.MakeRaw(\"dani\", \"parent\", \"emily\", \"\"),\n\t\tquad.MakeRaw(\"fred\", \"follows\", \"alice\", \"\"),\n\t\tquad.MakeRaw(\"greg\", \"follows\", \"alice\", \"\"),\n\t},\n}\n\nfunc TestRecursiveNext(t *testing.T) {\n\tctx := context.TODO()\n\tqs := recTestQs\n\tstart := NewFixed()\n\tstart.Add(refs.PreFetched(quad.Raw(\"alice\")))\n\tr := NewRecursive(start, singleHop(qs, \"parent\"), 0).Iterate()\n\n\texpected := []string{\"bob\", \"charlie\", \"dani\", \"emily\"}\n\tvar got []string\n\tfor r.Next(ctx) {\n\t\tqn, err := qs.NameOf(r.Result())\n\t\trequire.NoError(t, err)\n\t\tgot = append(got, quad.ToString(qn))\n\t}\n\tsort.Strings(expected)\n\tsort.Strings(got)\n\trequire.Equal(t, expected, got)\n}\n\nfunc TestRecursiveContains(t *testing.T) {\n\tctx := context.TODO()\n\tqs := recTestQs\n\tstart := NewFixed()\n\tstart.Add(refs.PreFetched(quad.Raw(\"alice\")))\n\tr := NewRecursive(start, singleHop(qs, \"parent\"), 0).Lookup()\n\tvalues := []string{\"charlie\", \"bob\", \"not\"}\n\texpected := []bool{true, true, false}\n\n\tfor i, v := range values {\n\t\tvn, err := qs.ValueOf(quad.Raw(v))\n\t\trequire.NoError(t, err)\n\t\tok := r.Contains(ctx, vn)\n\t\trequire.Equal(t, expected[i], ok)\n\t}\n}\n\nfunc TestRecursiveNextPath(t *testing.T) {\n\tctx := context.TODO()\n\tqs := recTestQs\n\tstart := qs.NodesAllIterator()\n\tstart = Tag(start, \"person\")\n\tit := singleHop(qs, \"follows\")(start)\n\tand := NewAnd()\n\tand.AddSubIterator(it)\n\tfixed := NewFixed()\n\tfixed.Add(refs.PreFetched(quad.Raw(\"alice\")))\n\tand.AddSubIterator(fixed)\n\tr := NewRecursive(and, singleHop(qs, \"parent\"), 0).Iterate()\n\n\texpected := []string{\"fred\", \"fred\", \"fred\", \"fred\", \"greg\", \"greg\", \"greg\", \"greg\"}\n\tvar got []string\n\tfor r.Next(ctx) {\n\t\tres := make(map[string]refs.Ref)\n\t\tr.TagResults(res)\n\t\tvn, err := qs.NameOf(res[\"person\"])\n\t\trequire.NoError(t, err)\n\t\tgot = append(got, quad.ToString(vn))\n\t\tfor r.NextPath(ctx) {\n\t\t\tres := make(map[string]refs.Ref)\n\t\t\tr.TagResults(res)\n\t\t\tvn, err := qs.NameOf(res[\"person\"])\n\t\t\trequire.NoError(t, err)\n\t\t\tgot = append(got, quad.ToString(vn))\n\t\t}\n\t}\n\tsort.Strings(expected)\n\tsort.Strings(got)\n\trequire.Equal(t, expected, got)\n}\n"
  },
  {
    "path": "graph/iterator/regex.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc newRegex(qs refs.Namer, sub Shape, re *regexp.Regexp, refs bool) Shape {\n\treturn NewValueFilter(qs, sub, func(v quad.Value) (bool, error) {\n\t\tswitch v := v.(type) {\n\t\tcase quad.String:\n\t\t\treturn re.MatchString(string(v)), nil\n\t\tcase quad.LangString:\n\t\t\treturn re.MatchString(string(v.Value)), nil\n\t\tcase quad.TypedString:\n\t\t\treturn re.MatchString(string(v.Value)), nil\n\t\tdefault:\n\t\t\tif refs {\n\t\t\t\tswitch v := v.(type) {\n\t\t\t\tcase quad.BNode:\n\t\t\t\t\treturn re.MatchString(string(v)), nil\n\t\t\t\tcase quad.IRI:\n\t\t\t\t\treturn re.MatchString(string(v)), nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false, nil\n\t})\n}\n\n// NewRegex returns an unary operator -- a filter across the values in the relevant\n// subiterator. It works similarly to gremlin's filter{it.matches('exp')},\n// reducing the iterator set to values whose string representation passes a\n// regular expression test.\nfunc NewRegex(sub Shape, re *regexp.Regexp, qs refs.Namer) Shape {\n\treturn newRegex(qs, sub, re, false)\n}\n\n// NewRegexWithRefs is like NewRegex but allows regexp iterator to match IRIs and BNodes.\n//\n// Consider using it carefully. In most cases it's better to reconsider\n// your graph structure instead of relying on slow unoptimizable regexp.\n//\n// An example of incorrect usage is to match IRIs:\n// \t<http://example.org/page>\n// \t<http://example.org/page/foo>\n// Via regexp like:\n//\thttp://example.org/page.*\n//\n// The right way is to explicitly link graph nodes and query them by this relation:\n// \t<http://example.org/page/foo> <type> <http://example.org/page>\nfunc NewRegexWithRefs(sub Shape, re *regexp.Regexp, qs refs.Namer) Shape {\n\treturn newRegex(qs, sub, re, true)\n}\n"
  },
  {
    "path": "graph/iterator/resolver.go",
    "content": "// Copyright 2018 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// A Resolver iterator consists of it's order, an index (where it is in the,\n// process of iterating) and a store to resolve values from.\ntype Resolver struct {\n\tqs    refs.Namer\n\torder []quad.Value\n}\n\n// NewResolver creates a new Resolver iterator.\nfunc NewResolver(qs refs.Namer, nodes ...quad.Value) *Resolver {\n\tit := &Resolver{\n\t\tqs:    qs,\n\t\torder: make([]quad.Value, len(nodes)),\n\t}\n\tcopy(it.order, nodes)\n\treturn it\n}\n\nfunc (it *Resolver) Iterate() Scanner {\n\treturn newResolverNext(it.qs, it.order)\n}\n\nfunc (it *Resolver) Lookup() Index {\n\treturn newResolverContains(it.qs, it.order)\n}\n\nfunc (it *Resolver) String() string {\n\treturn fmt.Sprintf(\"Resolver(%v)\", it.order)\n}\n\nfunc (it *Resolver) SubIterators() []Shape {\n\treturn nil\n}\n\n// Returns a Null iterator if it's empty so that upstream iterators can optimize it\n// away, otherwise there is no optimization.\nfunc (it *Resolver) Optimize(ctx context.Context) (Shape, bool) {\n\tif len(it.order) == 0 {\n\t\treturn NewNull(), true\n\t}\n\treturn it, false\n}\n\nfunc (it *Resolver) Stats(ctx context.Context) (Costs, error) {\n\treturn Costs{\n\t\t// Next is (presumably) O(1) from store\n\t\tNextCost:     1,\n\t\tContainsCost: 1,\n\t\tSize: refs.Size{\n\t\t\tValue: int64(len(it.order)),\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\n// A Resolver iterator consists of it's order, an index (where it is in the,\n// process of iterating) and a store to resolve values from.\ntype resolverNext struct {\n\tqs     refs.Namer\n\torder  []quad.Value\n\tvalues []refs.Ref\n\tcached bool\n\tindex  int\n\terr    error\n\tresult refs.Ref\n}\n\n// Creates a new Resolver iterator.\nfunc newResolverNext(qs refs.Namer, nodes []quad.Value) *resolverNext {\n\tit := &resolverNext{\n\t\tqs:    qs,\n\t\torder: make([]quad.Value, len(nodes)),\n\t}\n\tcopy(it.order, nodes)\n\treturn it\n}\n\nfunc (it *resolverNext) Close() error {\n\treturn nil\n}\n\nfunc (it *resolverNext) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *resolverNext) String() string {\n\treturn fmt.Sprintf(\"ResolverNext(%v, %v)\", it.order, it.values)\n}\n\n// Resolve nodes to values\nfunc (it *resolverNext) resolve(ctx context.Context) error {\n\tvalues, err := refs.RefsOf(ctx, it.qs, it.order)\n\tif err != nil {\n\t\treturn err\n\t}\n\tit.values = make([]refs.Ref, len(it.order))\n\tcopy(it.values, values)\n\tit.order = nil\n\tit.cached = true\n\treturn nil\n}\n\n// Next advances the iterator.\nfunc (it *resolverNext) Next(ctx context.Context) bool {\n\tif !it.cached {\n\t\tit.err = it.resolve(ctx)\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\tif it.index >= len(it.values) {\n\t\tit.result = nil\n\t\treturn false\n\t}\n\tit.result = it.values[it.index]\n\tit.index++\n\treturn true\n}\n\nfunc (it *resolverNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *resolverNext) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *resolverNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// A Resolver iterator consists of it's order, an index (where it is in the,\n// process of iterating) and a store to resolve values from.\ntype resolverContains struct {\n\tqs     refs.Namer\n\torder  []quad.Value\n\tnodes  map[interface{}]quad.Value\n\tcached bool\n\terr    error\n\tresult refs.Ref\n}\n\n// Creates a new Resolver iterator.\nfunc newResolverContains(qs refs.Namer, nodes []quad.Value) *resolverContains {\n\tit := &resolverContains{\n\t\tqs:    qs,\n\t\torder: make([]quad.Value, len(nodes)),\n\t}\n\tcopy(it.order, nodes)\n\treturn it\n}\n\nfunc (it *resolverContains) Close() error {\n\treturn nil\n}\n\nfunc (it *resolverContains) TagResults(dst map[string]refs.Ref) {}\n\nfunc (it *resolverContains) String() string {\n\treturn fmt.Sprintf(\"ResolverContains(%v, %v)\", it.order, it.nodes)\n}\n\n// Resolve nodes to values\nfunc (it *resolverContains) resolve(ctx context.Context) error {\n\tvalues, err := refs.RefsOf(ctx, it.qs, it.order)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Generally there are going to be no/few duplicates given\n\t// so allocate maps large enough to accommodate all\n\tit.nodes = make(map[interface{}]quad.Value, len(it.order))\n\tfor index, value := range values {\n\t\tnode := it.order[index]\n\t\tit.nodes[value.Key()] = node\n\t}\n\tit.order = nil\n\tit.cached = true\n\treturn nil\n}\n\n// Check if the passed value is equal to one of the order stored in the iterator.\nfunc (it *resolverContains) Contains(ctx context.Context, value refs.Ref) bool {\n\tif !it.cached {\n\t\tit.err = it.resolve(ctx)\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\t_, ok := it.nodes[value.Key()]\n\tif ok {\n\t\tit.result = value\n\t}\n\treturn ok\n}\n\nfunc (it *resolverContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *resolverContains) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *resolverContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n"
  },
  {
    "path": "graph/iterator/resolver_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph/graphmock\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc TestResolverIteratorIterate(t *testing.T) {\n\tvar ctx context.Context\n\tnodes := []quad.Value{\n\t\tquad.String(\"1\"),\n\t\tquad.String(\"2\"),\n\t\tquad.String(\"3\"),\n\t\tquad.String(\"4\"),\n\t\tquad.String(\"5\"),\n\t\tquad.String(\"3\"), // Assert iterator can handle duplicate values\n\t}\n\tdata := make([]quad.Quad, 0, len(nodes))\n\tfor _, node := range nodes {\n\t\tdata = append(data, quad.Make(quad.String(\"0\"), \"has\", node, nil))\n\t}\n\tqs := &graphmock.Store{\n\t\tData: data,\n\t}\n\texpected := make(map[quad.Value]refs.Ref)\n\tfor _, node := range nodes {\n\t\tvar err error\n\t\texpected[node], err = qs.ValueOf(node)\n\t\trequire.NoError(t, err)\n\t}\n\tit := iterator.NewResolver(qs, nodes...).Iterate()\n\tfor _, node := range nodes {\n\t\trequire.True(t, it.Next(ctx))\n\t\trequire.NoError(t, it.Err())\n\t\trequire.Equal(t, expected[node], it.Result())\n\t}\n\trequire.False(t, it.Next(ctx))\n\trequire.Nil(t, it.Result())\n}\n\nfunc TestResolverIteratorNotFoundError(t *testing.T) {\n\tvar ctx context.Context\n\tnodes := []quad.Value{\n\t\tquad.String(\"1\"),\n\t\tquad.String(\"2\"),\n\t\tquad.String(\"3\"),\n\t\tquad.String(\"4\"),\n\t\tquad.String(\"5\"),\n\t}\n\tdata := make([]quad.Quad, 0)\n\tskip := 3\n\tfor i, node := range nodes {\n\t\t// Simulate a missing subject\n\t\tif i == skip {\n\t\t\tcontinue\n\t\t}\n\t\tdata = append(data, quad.Make(quad.String(\"0\"), \"has\", node, nil))\n\t}\n\tqs := &graphmock.Store{\n\t\tData: data,\n\t}\n\tcount := 0\n\tit := iterator.NewResolver(qs, nodes...).Iterate()\n\tfor it.Next(ctx) {\n\t\tcount++\n\t}\n\trequire.Equal(t, 0, count)\n\trequire.Error(t, it.Err())\n\trequire.Nil(t, it.Result())\n}\n\nfunc TestResolverIteratorContains(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tnodes    []quad.Value\n\t\tsubject  quad.Value\n\t\tcontains bool\n\t}{\n\t\t{\n\t\t\t\"contains\",\n\t\t\t[]quad.Value{\n\t\t\t\tquad.String(\"1\"),\n\t\t\t\tquad.String(\"2\"),\n\t\t\t\tquad.String(\"3\"),\n\t\t\t},\n\t\t\tquad.String(\"2\"),\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t\"not contains\",\n\t\t\t[]quad.Value{\n\t\t\t\tquad.String(\"1\"),\n\t\t\t\tquad.String(\"3\"),\n\t\t\t},\n\t\t\tquad.String(\"2\"),\n\t\t\tfalse,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvar ctx context.Context\n\t\t\tdata := make([]quad.Quad, 0, len(test.nodes))\n\t\t\tfor _, node := range test.nodes {\n\t\t\t\tdata = append(data, quad.Make(quad.String(\"0\"), \"has\", node, nil))\n\t\t\t}\n\t\t\tqs := &graphmock.Store{\n\t\t\t\tData: data,\n\t\t\t}\n\t\t\tit := iterator.NewResolver(qs, test.nodes...).Lookup()\n\t\t\trequire.Equal(t, test.contains, it.Contains(ctx, refs.PreFetched(test.subject)))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "graph/iterator/save.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nvar (\n\t_ TaggerBase = (*Save)(nil)\n)\n\nfunc Tag(it Shape, tag string) Shape {\n\tif s, ok := it.(TaggerShape); ok {\n\t\ts.AddTags(tag)\n\t\treturn s\n\t} else if s, ok := it.(TaggerShape); ok {\n\t\ts.AddTags(tag)\n\t\treturn s\n\t}\n\treturn NewSave(it, tag)\n}\n\nvar (\n\t_ Shape       = (*Save)(nil)\n\t_ TaggerShape = (*Save)(nil)\n)\n\nfunc NewSave(on Shape, tags ...string) *Save {\n\ts := &Save{it: on}\n\ts.AddTags(tags...)\n\treturn s\n}\n\ntype Save struct {\n\tit        Shape\n\ttags      []string\n\tfixedTags map[string]refs.Ref\n}\n\nfunc (it *Save) Iterate() Scanner {\n\treturn newSaveNext(it.it.Iterate(), it.tags, it.fixedTags)\n}\n\nfunc (it *Save) Lookup() Index {\n\treturn newSaveContains(it.it.Lookup(), it.tags, it.fixedTags)\n}\n\nfunc (it *Save) String() string {\n\treturn fmt.Sprintf(\"Save(%v, %v)\", it.tags, it.fixedTags)\n}\n\n// Add a tag to the iterator.\nfunc (it *Save) AddTags(tag ...string) {\n\tit.tags = append(it.tags, tag...)\n}\n\nfunc (it *Save) AddFixedTag(tag string, value refs.Ref) {\n\tif it.fixedTags == nil {\n\t\tit.fixedTags = make(map[string]refs.Ref)\n\t}\n\tit.fixedTags[tag] = value\n}\n\n// Tags returns the tags held in the tagger. The returned value must not be mutated.\nfunc (it *Save) Tags() []string {\n\treturn it.tags\n}\n\n// Fixed returns the fixed tags held in the tagger. The returned value must not be mutated.\nfunc (it *Save) FixedTags() map[string]refs.Ref {\n\treturn it.fixedTags\n}\n\nfunc (it *Save) CopyFromTagger(st TaggerBase) {\n\tit.tags = append(it.tags, st.Tags()...)\n\n\tfixed := st.FixedTags()\n\tif len(fixed) == 0 {\n\t\treturn\n\t}\n\tif it.fixedTags == nil {\n\t\tit.fixedTags = make(map[string]refs.Ref, len(fixed))\n\t}\n\tfor k, v := range fixed {\n\t\tit.fixedTags[k] = v\n\t}\n}\n\nfunc (it *Save) Stats(ctx context.Context) (Costs, error) {\n\treturn it.it.Stats(ctx)\n}\n\nfunc (it *Save) Optimize(ctx context.Context) (nit Shape, no bool) {\n\tsub, ok := it.it.Optimize(ctx)\n\tif len(it.tags) == 0 && len(it.fixedTags) == 0 {\n\t\treturn sub, true\n\t}\n\tif st, ok2 := sub.(TaggerShape); ok2 {\n\t\tst.CopyFromTagger(it)\n\t\treturn st, true\n\t}\n\tif !ok {\n\t\treturn it, false\n\t}\n\ts := NewSave(sub)\n\ts.CopyFromTagger(it)\n\treturn s, true\n}\n\nfunc (it *Save) SubIterators() []Shape {\n\treturn []Shape{it.it}\n}\n\nfunc newSaveNext(it Scanner, tags []string, fixed map[string]refs.Ref) *saveNext {\n\treturn &saveNext{it: it, tags: tags, fixedTags: fixed}\n}\n\ntype saveNext struct {\n\tit        Scanner\n\ttags      []string\n\tfixedTags map[string]refs.Ref\n}\n\nfunc (it *saveNext) String() string {\n\treturn fmt.Sprintf(\"Save(%v, %v)\", it.tags, it.fixedTags)\n}\n\nfunc (it *saveNext) TagResults(dst map[string]refs.Ref) {\n\tit.it.TagResults(dst)\n\n\tv := it.Result()\n\tfor _, tag := range it.tags {\n\t\tdst[tag] = v\n\t}\n\n\tfor tag, value := range it.fixedTags {\n\t\tdst[tag] = value\n\t}\n}\n\nfunc (it *saveNext) Result() refs.Ref {\n\treturn it.it.Result()\n}\n\nfunc (it *saveNext) Next(ctx context.Context) bool {\n\treturn it.it.Next(ctx)\n}\n\nfunc (it *saveNext) NextPath(ctx context.Context) bool {\n\treturn it.it.NextPath(ctx)\n}\n\nfunc (it *saveNext) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *saveNext) Close() error {\n\treturn it.it.Close()\n}\n\nfunc newSaveContains(it Index, tags []string, fixed map[string]refs.Ref) *saveContains {\n\treturn &saveContains{it: it, tags: tags, fixed: fixed}\n}\n\ntype saveContains struct {\n\tit    Index\n\ttags  []string\n\tfixed map[string]refs.Ref\n}\n\nfunc (it *saveContains) String() string {\n\treturn fmt.Sprintf(\"SaveContains(%v, %v)\", it.tags, it.fixed)\n}\n\nfunc (it *saveContains) TagResults(dst map[string]refs.Ref) {\n\tit.it.TagResults(dst)\n\n\tv := it.Result()\n\tfor _, tag := range it.tags {\n\t\tdst[tag] = v\n\t}\n\n\tfor tag, value := range it.fixed {\n\t\tdst[tag] = value\n\t}\n}\n\nfunc (it *saveContains) Result() refs.Ref {\n\treturn it.it.Result()\n}\n\nfunc (it *saveContains) NextPath(ctx context.Context) bool {\n\treturn it.it.NextPath(ctx)\n}\n\nfunc (it *saveContains) Contains(ctx context.Context, v refs.Ref) bool {\n\treturn it.it.Contains(ctx, v)\n}\n\nfunc (it *saveContains) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *saveContains) Close() error {\n\treturn it.it.Close()\n}\n"
  },
  {
    "path": "graph/iterator/skip.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Skip iterator will skip certain number of values from primary iterator.\ntype Skip struct {\n\tskip      int64\n\tprimaryIt Shape\n}\n\nfunc NewSkip(primaryIt Shape, off int64) *Skip {\n\treturn &Skip{\n\t\tskip:      off,\n\t\tprimaryIt: primaryIt,\n\t}\n}\n\nfunc (it *Skip) Iterate() Scanner {\n\treturn newSkipNext(it.primaryIt.Iterate(), it.skip)\n}\n\nfunc (it *Skip) Lookup() Index {\n\treturn newSkipContains(it.primaryIt.Lookup(), it.skip)\n}\n\n// SubIterators returns a slice of the sub iterators.\nfunc (it *Skip) SubIterators() []Shape {\n\treturn []Shape{it.primaryIt}\n}\n\nfunc (it *Skip) Optimize(ctx context.Context) (Shape, bool) {\n\toptimizedPrimaryIt, optimized := it.primaryIt.Optimize(ctx)\n\tif it.skip == 0 { // nothing to skip\n\t\treturn optimizedPrimaryIt, true\n\t}\n\tit.primaryIt = optimizedPrimaryIt\n\treturn it, optimized\n}\n\nfunc (it *Skip) Stats(ctx context.Context) (Costs, error) {\n\tprimaryStats, err := it.primaryIt.Stats(ctx)\n\tif primaryStats.Size.Exact {\n\t\tprimaryStats.Size.Value -= it.skip\n\t\tif primaryStats.Size.Value < 0 {\n\t\t\tprimaryStats.Size.Value = 0\n\t\t}\n\t}\n\treturn primaryStats, err\n}\n\nfunc (it *Skip) String() string {\n\treturn fmt.Sprintf(\"Skip(%d)\", it.skip)\n}\n\n// Skip iterator will skip certain number of values from primary iterator.\ntype skipNext struct {\n\tskip      int64\n\tskipped   int64\n\tprimaryIt Scanner\n}\n\nfunc newSkipNext(primaryIt Scanner, skip int64) *skipNext {\n\treturn &skipNext{\n\t\tskip:      skip,\n\t\tprimaryIt: primaryIt,\n\t}\n}\n\nfunc (it *skipNext) TagResults(dst map[string]refs.Ref) {\n\tit.primaryIt.TagResults(dst)\n}\n\n// Next advances the Skip iterator. It will skip all initial values\n// before returning actual result.\nfunc (it *skipNext) Next(ctx context.Context) bool {\n\tfor ; it.skipped < it.skip; it.skipped++ {\n\t\tif !it.primaryIt.Next(ctx) {\n\t\t\treturn false\n\t\t}\n\t}\n\tif it.primaryIt.Next(ctx) {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *skipNext) Err() error {\n\treturn it.primaryIt.Err()\n}\n\nfunc (it *skipNext) Result() refs.Ref {\n\treturn it.primaryIt.Result()\n}\n\n// NextPath checks whether there is another path. It will skip first paths\n// according to iterator parameter.\nfunc (it *skipNext) NextPath(ctx context.Context) bool {\n\tfor ; it.skipped < it.skip; it.skipped++ {\n\t\tif !it.primaryIt.NextPath(ctx) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn it.primaryIt.NextPath(ctx)\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *skipNext) Close() error {\n\treturn it.primaryIt.Close()\n}\n\nfunc (it *skipNext) String() string {\n\treturn fmt.Sprintf(\"SkipNext(%d)\", it.skip)\n}\n\n// Skip iterator will skip certain number of values from primary iterator.\ntype skipContains struct {\n\tskip      int64\n\tskipped   int64\n\tprimaryIt Index\n}\n\nfunc newSkipContains(primaryIt Index, skip int64) *skipContains {\n\treturn &skipContains{\n\t\tskip:      skip,\n\t\tprimaryIt: primaryIt,\n\t}\n}\n\nfunc (it *skipContains) TagResults(dst map[string]refs.Ref) {\n\tit.primaryIt.TagResults(dst)\n}\n\nfunc (it *skipContains) Err() error {\n\treturn it.primaryIt.Err()\n}\n\nfunc (it *skipContains) Result() refs.Ref {\n\treturn it.primaryIt.Result()\n}\n\nfunc (it *skipContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tinNextPath := false\n\tfor it.skipped <= it.skip {\n\t\t// skipping main iterator results\n\t\tinNextPath = false\n\t\tif !it.primaryIt.Contains(ctx, val) {\n\t\t\treturn false\n\t\t}\n\t\tit.skipped++\n\n\t\t// TODO(dennwc): we don't really know if we should call NextPath or not,\n\t\t//               and there is no good way to know\n\t\tif it.skipped <= it.skip {\n\t\t\t// skipping NextPath results\n\t\t\tinNextPath = true\n\t\t\tif !it.primaryIt.NextPath(ctx) {\n\t\t\t\t// main path exists, but we skipped it\n\t\t\t\t// and we skipped all alternative paths now\n\t\t\t\t// so we definitely \"don't have\" this value\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tit.skipped++\n\n\t\t\tfor it.skipped <= it.skip {\n\t\t\t\tif !it.primaryIt.NextPath(ctx) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tit.skipped++\n\t\t\t}\n\t\t}\n\t}\n\tif inNextPath && it.primaryIt.NextPath(ctx) {\n\t\treturn true\n\t}\n\treturn it.primaryIt.Contains(ctx, val)\n}\n\n// NextPath checks whether there is another path. It will skip first paths\n// according to iterator parameter.\nfunc (it *skipContains) NextPath(ctx context.Context) bool {\n\tfor ; it.skipped < it.skip; it.skipped++ {\n\t\tif !it.primaryIt.NextPath(ctx) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn it.primaryIt.NextPath(ctx)\n}\n\n// Close closes the primary and all iterators.  It closes all subiterators\n// it can, but returns the first error it encounters.\nfunc (it *skipContains) Close() error {\n\treturn it.primaryIt.Close()\n}\n\nfunc (it *skipContains) String() string {\n\treturn fmt.Sprintf(\"SkipContains(%d)\", it.skip)\n}\n"
  },
  {
    "path": "graph/iterator/skip_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nfunc TestSkipIteratorBasics(t *testing.T) {\n\tctx := context.TODO()\n\tallIt := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(4),\n\t\tInt64Node(5),\n\t)\n\n\tu := NewSkip(allIt, 0)\n\texpectSz, _ := allIt.Stats(ctx)\n\tsz, _ := u.Stats(ctx)\n\trequire.Equal(t, expectSz.Size.Value, sz.Size.Value)\n\n\trequire.Equal(t, []int{1, 2, 3, 4, 5}, iterated(u))\n\n\tu = NewSkip(allIt, 3)\n\texpectSz.Size.Value = 2\n\tif sz, _ := u.Stats(ctx); sz.Size.Value != expectSz.Size.Value {\n\t\tt.Errorf(\"Failed to check Skip size: got:%v expected:%v\", sz.Size, expectSz.Size)\n\t}\n\trequire.Equal(t, []int{4, 5}, iterated(u))\n\n\tuc := u.Lookup()\n\tfor _, v := range []int{1, 2, 3} {\n\t\trequire.False(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\tfor _, v := range []int{4, 5} {\n\t\trequire.True(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\n\tuc = u.Lookup()\n\tfor _, v := range []int{5, 4, 3} {\n\t\trequire.False(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\tfor _, v := range []int{1, 2} {\n\t\trequire.True(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n\n\t// TODO(dennwc): check with NextPath\n}\n"
  },
  {
    "path": "graph/iterator/sort.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\t\"sort\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Sort iterator orders values from it's subiterator.\ntype Sort struct {\n\tnamer refs.Namer\n\tsubIt Shape\n}\n\n// NewSort creates a new Sort iterator.\n// TODO(dennwc): This iterator must not be used inside And: it may be moved to a Contains branch and won't do anything.\n//               We should make And/Intersect account for this.\nfunc NewSort(namer refs.Namer, subIt Shape) *Sort {\n\treturn &Sort{namer, subIt}\n}\n\nfunc (it *Sort) Iterate() Scanner {\n\treturn newSortNext(it.namer, it.subIt.Iterate())\n}\n\nfunc (it *Sort) Lookup() Index {\n\t// TODO(dennwc): Lookup doesn't need any sorting. Using it this way is a bug in the optimizer.\n\t//               But instead of failing here, let still allow the query to execute. It won't be sorted,\n\t//               but it will work at least. Later consider changing returning an error here.\n\treturn it.subIt.Lookup()\n}\n\nfunc (it *Sort) Optimize(ctx context.Context) (Shape, bool) {\n\tnewIt, optimized := it.subIt.Optimize(ctx)\n\tif optimized {\n\t\tit.subIt = newIt\n\t}\n\treturn it, false\n}\n\nfunc (it *Sort) Stats(ctx context.Context) (Costs, error) {\n\tsubStats, err := it.subIt.Stats(ctx)\n\treturn Costs{\n\t\t// TODO(dennwc): better cost calculation; we probably need an InitCost defined in Costs\n\t\tNextCost:     subStats.NextCost * 2,\n\t\tContainsCost: subStats.ContainsCost,\n\t\tSize: refs.Size{\n\t\t\tValue: subStats.Size.Value,\n\t\t\tExact: true,\n\t\t},\n\t}, err\n}\n\nfunc (it *Sort) String() string {\n\treturn \"Sort\"\n}\n\n// SubIterators returns a slice of the sub iterators.\nfunc (it *Sort) SubIterators() []Shape {\n\treturn []Shape{it.subIt}\n}\n\ntype sortValue struct {\n\tresult\n\tstr   string\n\tpaths []result\n}\ntype sortByString []sortValue\n\nfunc (v sortByString) Len() int { return len(v) }\nfunc (v sortByString) Less(i, j int) bool {\n\treturn v[i].str < v[j].str\n}\nfunc (v sortByString) Swap(i, j int) { v[i], v[j] = v[j], v[i] }\n\ntype sortNext struct {\n\tnamer     refs.Namer\n\tsubIt     Scanner\n\tordered   sortByString\n\tresult    result\n\terr       error\n\tindex     int\n\tpathIndex int\n}\n\nfunc newSortNext(namer refs.Namer, subIt Scanner) *sortNext {\n\treturn &sortNext{\n\t\tnamer:     namer,\n\t\tsubIt:     subIt,\n\t\tpathIndex: -1,\n\t}\n}\n\nfunc (it *sortNext) TagResults(dst map[string]refs.Ref) {\n\tfor tag, value := range it.result.tags {\n\t\tdst[tag] = value\n\t}\n}\n\nfunc (it *sortNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *sortNext) Result() refs.Ref {\n\treturn it.result.id\n}\n\nfunc (it *sortNext) Next(ctx context.Context) bool {\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif it.ordered == nil {\n\t\tv, err := getSortedValues(ctx, it.namer, it.subIt)\n\t\tit.ordered = v\n\t\tit.err = err\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\tif it.index >= len(it.ordered) {\n\t\treturn false\n\t}\n\tit.pathIndex = -1\n\tit.result = it.ordered[it.index].result\n\tit.index++\n\treturn true\n}\n\nfunc (it *sortNext) NextPath(ctx context.Context) bool {\n\tif it.index >= len(it.ordered) {\n\t\treturn false\n\t}\n\tr := it.ordered[it.index]\n\tif it.pathIndex+1 >= len(r.paths) {\n\t\treturn false\n\t}\n\tit.pathIndex++\n\tit.result = r.paths[it.pathIndex]\n\treturn true\n}\n\nfunc (it *sortNext) Close() error {\n\tit.ordered = nil\n\treturn it.subIt.Close()\n}\n\nfunc (it *sortNext) String() string {\n\treturn \"SortNext\"\n}\n\nfunc getSortedValues(ctx context.Context, namer refs.Namer, it Scanner) (sortByString, error) {\n\tvar v sortByString\n\tfor it.Next(ctx) {\n\t\tid := it.Result()\n\t\t// TODO(dennwc): batch and use refs.ValuesOf\n\t\tname, err := namer.NameOf(id)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tstr := name.String()\n\t\ttags := make(map[string]refs.Ref)\n\t\tit.TagResults(tags)\n\t\tval := sortValue{\n\t\t\tresult: result{id, tags},\n\t\t\tstr:    str,\n\t\t}\n\t\tfor it.NextPath(ctx) {\n\t\t\ttags = make(map[string]refs.Ref)\n\t\t\tit.TagResults(tags)\n\t\t\tval.paths = append(val.paths, result{id, tags})\n\t\t}\n\t\tv = append(v, val)\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn v, err\n\t}\n\tsort.Sort(v)\n\treturn v, nil\n}\n"
  },
  {
    "path": "graph/iterator/unique.go",
    "content": "package iterator\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\n// Unique iterator removes duplicate values from it's subiterator.\ntype Unique struct {\n\tsubIt Shape\n}\n\nfunc NewUnique(subIt Shape) *Unique {\n\treturn &Unique{\n\t\tsubIt: subIt,\n\t}\n}\n\nfunc (it *Unique) Iterate() Scanner {\n\treturn newUniqueNext(it.subIt.Iterate())\n}\n\nfunc (it *Unique) Lookup() Index {\n\treturn newUniqueContains(it.subIt.Lookup())\n}\n\n// SubIterators returns a slice of the sub iterators. The first iterator is the\n// primary iterator, for which the complement is generated.\nfunc (it *Unique) SubIterators() []Shape {\n\treturn []Shape{it.subIt}\n}\n\nfunc (it *Unique) Optimize(ctx context.Context) (Shape, bool) {\n\tnewIt, optimized := it.subIt.Optimize(ctx)\n\tif optimized {\n\t\tit.subIt = newIt\n\t}\n\treturn it, false\n}\n\nconst uniquenessFactor = 2\n\nfunc (it *Unique) Stats(ctx context.Context) (Costs, error) {\n\tsubStats, err := it.subIt.Stats(ctx)\n\treturn Costs{\n\t\tNextCost:     subStats.NextCost * uniquenessFactor,\n\t\tContainsCost: subStats.ContainsCost,\n\t\tSize: refs.Size{\n\t\t\tValue: subStats.Size.Value / uniquenessFactor,\n\t\t\tExact: false,\n\t\t},\n\t}, err\n}\n\nfunc (it *Unique) String() string {\n\treturn \"Unique\"\n}\n\n// Unique iterator removes duplicate values from it's subiterator.\ntype uniqueNext struct {\n\tsubIt  Scanner\n\tresult refs.Ref\n\terr    error\n\tseen   map[interface{}]bool\n}\n\nfunc newUniqueNext(subIt Scanner) *uniqueNext {\n\treturn &uniqueNext{\n\t\tsubIt: subIt,\n\t\tseen:  make(map[interface{}]bool),\n\t}\n}\n\nfunc (it *uniqueNext) TagResults(dst map[string]refs.Ref) {\n\tif it.subIt != nil {\n\t\tit.subIt.TagResults(dst)\n\t}\n}\n\n// Next advances the subiterator, continuing until it returns a value which it\n// has not previously seen.\nfunc (it *uniqueNext) Next(ctx context.Context) bool {\n\tfor it.subIt.Next(ctx) {\n\t\tcurr := it.subIt.Result()\n\t\tkey := refs.ToKey(curr)\n\t\tif ok := it.seen[key]; !ok {\n\t\t\tit.result = curr\n\t\t\tit.seen[key] = true\n\t\t\treturn true\n\t\t}\n\t}\n\tit.err = it.subIt.Err()\n\treturn false\n}\n\nfunc (it *uniqueNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *uniqueNext) Result() refs.Ref {\n\treturn it.result\n}\n\n// NextPath for unique always returns false. If we were to return multiple\n// paths, we'd no longer be a unique result, so we have to choose only the first\n// path that got us here. Unique is serious on this point.\nfunc (it *uniqueNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// Close closes the primary iterators.\nfunc (it *uniqueNext) Close() error {\n\tit.seen = nil\n\treturn it.subIt.Close()\n}\n\nfunc (it *uniqueNext) String() string {\n\treturn \"UniqueNext\"\n}\n\n// Unique iterator removes duplicate values from it's subiterator.\ntype uniqueContains struct {\n\tsubIt Index\n}\n\nfunc newUniqueContains(subIt Index) *uniqueContains {\n\treturn &uniqueContains{\n\t\tsubIt: subIt,\n\t}\n}\n\nfunc (it *uniqueContains) TagResults(dst map[string]refs.Ref) {\n\tif it.subIt != nil {\n\t\tit.subIt.TagResults(dst)\n\t}\n}\n\nfunc (it *uniqueContains) Err() error {\n\treturn it.subIt.Err()\n}\n\nfunc (it *uniqueContains) Result() refs.Ref {\n\treturn it.subIt.Result()\n}\n\n// Contains checks whether the passed value is part of the primary iterator,\n// which is irrelevant for uniqueness.\nfunc (it *uniqueContains) Contains(ctx context.Context, val refs.Ref) bool {\n\treturn it.subIt.Contains(ctx, val)\n}\n\n// NextPath for unique always returns false. If we were to return multiple\n// paths, we'd no longer be a unique result, so we have to choose only the first\n// path that got us here. Unique is serious on this point.\nfunc (it *uniqueContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\n// Close closes the primary iterators.\nfunc (it *uniqueContains) Close() error {\n\treturn it.subIt.Close()\n}\n\nfunc (it *uniqueContains) String() string {\n\treturn \"UniqueContains\"\n}\n"
  },
  {
    "path": "graph/iterator/unique_test.go",
    "content": "package iterator_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nfunc TestUniqueIteratorBasics(t *testing.T) {\n\tctx := context.TODO()\n\tallIt := NewFixed(\n\t\tInt64Node(1),\n\t\tInt64Node(2),\n\t\tInt64Node(3),\n\t\tInt64Node(3),\n\t\tInt64Node(2),\n\t)\n\n\tu := NewUnique(allIt)\n\n\texpect := []int{1, 2, 3}\n\tfor i := 0; i < 2; i++ {\n\t\trequire.Equal(t, expect, iterated(u))\n\t}\n\n\tuc := u.Lookup()\n\tfor _, v := range []int{1, 2, 3} {\n\t\trequire.True(t, uc.Contains(ctx, Int64Node(v)))\n\t}\n}\n"
  },
  {
    "path": "graph/iterator/value_comparison.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\n// \"Value Comparison\" is a unary operator -- a filter across the values in the\n// relevant subiterator.\n//\n// This is hugely useful for things like label, but value ranges in general\n// come up from time to time. At *worst* we're as big as our underlying iterator.\n// At best, we're the null iterator.\n//\n// This is ripe for backend-side optimization. If you can run a value iterator,\n// from a sorted set -- some sort of value index, then go for it.\n//\n// In MQL terms, this is the [{\"age>=\": 21}] concept.\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype Operator int\n\nfunc (op Operator) String() string {\n\tswitch op {\n\tcase CompareLT:\n\t\treturn \"<\"\n\tcase CompareLTE:\n\t\treturn \"<=\"\n\tcase CompareGT:\n\t\treturn \">\"\n\tcase CompareGTE:\n\t\treturn \">=\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"op(%d)\", int(op))\n\t}\n}\n\nconst (\n\tCompareLT Operator = iota\n\tCompareLTE\n\tCompareGT\n\tCompareGTE\n\t// Why no Equals? Because that's usually an AndIterator.\n)\n\nfunc NewComparison(sub Shape, op Operator, val quad.Value, qs refs.Namer) Shape {\n\treturn NewValueFilter(qs, sub, func(qval quad.Value) (bool, error) {\n\t\tswitch cVal := val.(type) {\n\t\tcase quad.Int:\n\t\t\tif cVal2, ok := qval.(quad.Int); ok {\n\t\t\t\treturn RunIntOp(cVal2, op, cVal), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase quad.Float:\n\t\t\tif cVal2, ok := qval.(quad.Float); ok {\n\t\t\t\treturn RunFloatOp(cVal2, op, cVal), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase quad.String:\n\t\t\tif cVal2, ok := qval.(quad.String); ok {\n\t\t\t\treturn RunStrOp(string(cVal2), op, string(cVal)), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase quad.BNode:\n\t\t\tif cVal2, ok := qval.(quad.BNode); ok {\n\t\t\t\treturn RunStrOp(string(cVal2), op, string(cVal)), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase quad.IRI:\n\t\t\tif cVal2, ok := qval.(quad.IRI); ok {\n\t\t\t\treturn RunStrOp(string(cVal2), op, string(cVal)), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tcase quad.Time:\n\t\t\tif cVal2, ok := qval.(quad.Time); ok {\n\t\t\t\treturn RunTimeOp(time.Time(cVal2), op, time.Time(cVal)), nil\n\t\t\t}\n\t\t\treturn false, nil\n\t\tdefault:\n\t\t\treturn RunStrOp(quad.StringOf(qval), op, quad.StringOf(val)), nil\n\t\t}\n\t})\n}\n\nfunc RunIntOp(a quad.Int, op Operator, b quad.Int) bool {\n\tswitch op {\n\tcase CompareLT:\n\t\treturn a < b\n\tcase CompareLTE:\n\t\treturn a <= b\n\tcase CompareGT:\n\t\treturn a > b\n\tcase CompareGTE:\n\t\treturn a >= b\n\tdefault:\n\t\tpanic(\"Unknown operator type\")\n\t}\n}\n\nfunc RunFloatOp(a quad.Float, op Operator, b quad.Float) bool {\n\tswitch op {\n\tcase CompareLT:\n\t\treturn a < b\n\tcase CompareLTE:\n\t\treturn a <= b\n\tcase CompareGT:\n\t\treturn a > b\n\tcase CompareGTE:\n\t\treturn a >= b\n\tdefault:\n\t\tpanic(\"Unknown operator type\")\n\t}\n}\n\nfunc RunStrOp(a string, op Operator, b string) bool {\n\tswitch op {\n\tcase CompareLT:\n\t\treturn a < b\n\tcase CompareLTE:\n\t\treturn a <= b\n\tcase CompareGT:\n\t\treturn a > b\n\tcase CompareGTE:\n\t\treturn a >= b\n\tdefault:\n\t\tpanic(\"Unknown operator type\")\n\t}\n}\n\nfunc RunTimeOp(a time.Time, op Operator, b time.Time) bool {\n\tswitch op {\n\tcase CompareLT:\n\t\treturn a.Before(b)\n\tcase CompareLTE:\n\t\treturn !a.After(b)\n\tcase CompareGT:\n\t\treturn a.After(b)\n\tcase CompareGTE:\n\t\treturn !a.Before(b)\n\tdefault:\n\t\tpanic(\"Unknown operator type\")\n\t}\n}\n"
  },
  {
    "path": "graph/iterator/value_comparison_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph/graphmock\"\n\t. \"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar (\n\tsimpleStore = &graphmock.Oldstore{Data: []string{\"0\", \"1\", \"2\", \"3\", \"4\", \"5\"}, Parse: true}\n\tstringStore = &graphmock.Oldstore{Data: []string{\"foo\", \"bar\", \"baz\", \"echo\"}, Parse: true}\n\tmixedStore  = &graphmock.Oldstore{Data: []string{\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"foo\", \"bar\", \"baz\", \"echo\"}, Parse: true}\n)\n\nfunc simpleFixedIterator() *Fixed {\n\tf := NewFixed()\n\tfor i := 0; i < 5; i++ {\n\t\tf.Add(Int64Node(i))\n\t}\n\treturn f\n}\n\nfunc stringFixedIterator() *Fixed {\n\tf := NewFixed()\n\tfor _, value := range stringStore.Data {\n\t\tf.Add(graphmock.StringNode(value))\n\t}\n\treturn f\n}\n\nfunc mixedFixedIterator() *Fixed {\n\tf := NewFixed()\n\tfor i := 0; i < len(mixedStore.Data); i++ {\n\t\tf.Add(Int64Node(i))\n\t}\n\treturn f\n}\n\nvar comparisonTests = []struct {\n\tmessage  string\n\toperand  quad.Value\n\toperator Operator\n\texpect   []quad.Value\n\tqs       refs.Namer\n\titerator func() *Fixed\n}{\n\t{\n\t\tmessage:  \"successful int64 less than comparison\",\n\t\toperand:  quad.Int(3),\n\t\toperator: CompareLT,\n\t\texpect:   []quad.Value{quad.Int(0), quad.Int(1), quad.Int(2)},\n\t\tqs:       simpleStore,\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"empty int64 less than comparison\",\n\t\toperand:  quad.Int(0),\n\t\toperator: CompareLT,\n\t\texpect:   nil,\n\t\tqs:       simpleStore,\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful int64 greater than comparison\",\n\t\toperand:  quad.Int(2),\n\t\toperator: CompareGT,\n\t\texpect:   []quad.Value{quad.Int(3), quad.Int(4)},\n\t\tqs:       simpleStore,\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful int64 greater than or equal comparison\",\n\t\toperand:  quad.Int(2),\n\t\toperator: CompareGTE,\n\t\texpect:   []quad.Value{quad.Int(2), quad.Int(3), quad.Int(4)},\n\t\tqs:       simpleStore,\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful int64 greater than or equal comparison (mixed)\",\n\t\toperand:  quad.Int(2),\n\t\toperator: CompareGTE,\n\t\texpect:   []quad.Value{quad.Int(2), quad.Int(3), quad.Int(4), quad.Int(5)},\n\t\tqs:       mixedStore,\n\t\titerator: mixedFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful string less than comparison\",\n\t\toperand:  quad.String(\"echo\"),\n\t\toperator: CompareLT,\n\t\texpect:   []quad.Value{quad.String(\"bar\"), quad.String(\"baz\")},\n\t\tqs:       stringStore,\n\t\titerator: stringFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"empty string less than comparison\",\n\t\toperand:  quad.String(\"\"),\n\t\toperator: CompareLT,\n\t\texpect:   nil,\n\t\tqs:       stringStore,\n\t\titerator: stringFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful string greater than comparison\",\n\t\toperand:  quad.String(\"echo\"),\n\t\toperator: CompareGT,\n\t\texpect:   []quad.Value{quad.String(\"foo\")},\n\t\tqs:       stringStore,\n\t\titerator: stringFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"successful string greater than or equal comparison\",\n\t\toperand:  quad.String(\"echo\"),\n\t\toperator: CompareGTE,\n\t\texpect:   []quad.Value{quad.String(\"foo\"), quad.String(\"echo\")},\n\t\tqs:       stringStore,\n\t\titerator: stringFixedIterator,\n\t},\n}\n\nfunc TestValueComparison(t *testing.T) {\n\tctx := context.TODO()\n\tfor _, test := range comparisonTests {\n\t\tqs := test.qs\n\t\tvc := NewComparison(test.iterator(), test.operator, test.operand, qs).Iterate()\n\n\t\tvar got []quad.Value\n\t\tfor vc.Next(ctx) {\n\t\t\tqsv, err := qs.NameOf(vc.Result())\n\t\t\trequire.NoError(t, err)\n\t\t\tgot = append(got, qsv)\n\t\t}\n\t\tif !reflect.DeepEqual(got, test.expect) {\n\t\t\tt.Errorf(\"Failed to show %s, got:%q expect:%q\", test.message, got, test.expect)\n\t\t}\n\t}\n}\n\nvar vciContainsTests = []struct {\n\tmessage  string\n\toperator Operator\n\tcheck    refs.Ref\n\texpect   bool\n\tqs       refs.Namer\n\tval      quad.Value\n\titerator func() *Fixed\n}{\n\t{\n\t\tmessage:  \"1 is less than 2\",\n\t\toperator: CompareGTE,\n\t\tcheck:    Int64Node(1),\n\t\texpect:   false,\n\t\tqs:       simpleStore,\n\t\tval:      quad.Int(2),\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"2 is greater than or equal to 2\",\n\t\toperator: CompareGTE,\n\t\tcheck:    Int64Node(2),\n\t\texpect:   true,\n\t\tqs:       simpleStore,\n\t\tval:      quad.Int(2),\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"3 is greater than or equal to 2\",\n\t\toperator: CompareGTE,\n\t\tcheck:    Int64Node(3),\n\t\texpect:   true,\n\t\tqs:       simpleStore,\n\t\tval:      quad.Int(2),\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"5 is absent from iterator\",\n\t\toperator: CompareGTE,\n\t\tcheck:    Int64Node(5),\n\t\texpect:   false,\n\t\tqs:       simpleStore,\n\t\tval:      quad.Int(2),\n\t\titerator: simpleFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"foo is greater than or equal to echo\",\n\t\toperator: CompareGTE,\n\t\tcheck:    graphmock.StringNode(\"foo\"),\n\t\texpect:   true,\n\t\tqs:       stringStore,\n\t\tval:      quad.String(\"echo\"),\n\t\titerator: stringFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"echo is greater than or equal to echo\",\n\t\toperator: CompareGTE,\n\t\tcheck:    graphmock.StringNode(\"echo\"),\n\t\texpect:   true,\n\t\tqs:       stringStore,\n\t\tval:      quad.String(\"echo\"),\n\t\titerator: stringFixedIterator,\n\t},\n\t{\n\t\tmessage:  \"foo is missing from the iterator\",\n\t\toperator: CompareLTE,\n\t\tcheck:    graphmock.StringNode(\"foo\"),\n\t\texpect:   false,\n\t\tqs:       stringStore,\n\t\tval:      quad.String(\"echo\"),\n\t\titerator: stringFixedIterator,\n\t},\n}\n\nfunc TestVCIContains(t *testing.T) {\n\tctx := context.TODO()\n\tfor _, test := range vciContainsTests {\n\t\tvc := NewComparison(test.iterator(), test.operator, test.val, test.qs).Lookup()\n\t\tif vc.Contains(ctx, test.check) != test.expect {\n\t\t\tt.Errorf(\"Failed to show %s\", test.message)\n\t\t}\n\t}\n}\n\nvar comparisonIteratorTests = []struct {\n\tmessage string\n\tqs      refs.Namer\n\tval     quad.Value\n}{\n\t{\n\t\tmessage: \"2 is absent from iterator\",\n\t\tqs:      simpleStore,\n\t\tval:     quad.Int(2),\n\t},\n\t{\n\t\tmessage: \"'missing' is absent from iterator\",\n\t\tqs:      stringStore,\n\t\tval:     quad.String(\"missing\"),\n\t},\n}\n\nfunc TestComparisonIteratorErr(t *testing.T) {\n\tctx := context.TODO()\n\twantErr := errors.New(\"unique\")\n\terrIt := newTestIterator(false, wantErr)\n\n\tfor _, test := range comparisonIteratorTests {\n\t\tvc := NewComparison(errIt, CompareLT, test.val, test.qs).Iterate()\n\n\t\trequire.False(t, vc.Next(ctx))\n\t\trequire.Equal(t, wantErr, vc.Err())\n\t}\n}\n"
  },
  {
    "path": "graph/iterator/value_filter.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage iterator\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype ValueFilterFunc func(quad.Value) (bool, error)\n\ntype ValueFilter struct {\n\tsub    Shape\n\tfilter ValueFilterFunc\n\tqs     refs.Namer\n}\n\nfunc NewValueFilter(qs refs.Namer, sub Shape, filter ValueFilterFunc) *ValueFilter {\n\treturn &ValueFilter{\n\t\tsub:    sub,\n\t\tqs:     qs,\n\t\tfilter: filter,\n\t}\n}\n\nfunc (it *ValueFilter) Iterate() Scanner {\n\treturn newValueFilterNext(it.qs, it.sub.Iterate(), it.filter)\n}\n\nfunc (it *ValueFilter) Lookup() Index {\n\treturn newValueFilterContains(it.qs, it.sub.Lookup(), it.filter)\n}\n\nfunc (it *ValueFilter) SubIterators() []Shape {\n\treturn []Shape{it.sub}\n}\n\nfunc (it *ValueFilter) String() string {\n\treturn \"ValueFilter\"\n}\n\n// There's nothing to optimize, locally, for a value-comparison iterator.\n// Replace the underlying iterator if need be.\n// potentially replace it.\nfunc (it *ValueFilter) Optimize(ctx context.Context) (Shape, bool) {\n\tnewSub, changed := it.sub.Optimize(ctx)\n\tif changed {\n\t\tit.sub = newSub\n\t}\n\treturn it, true\n}\n\n// We're only as expensive as our subiterator.\n// Again, optimized value comparison iterators should do better.\nfunc (it *ValueFilter) Stats(ctx context.Context) (Costs, error) {\n\tst, err := it.sub.Stats(ctx)\n\tst.Size.Value = st.Size.Value/2 + 1\n\tst.Size.Exact = false\n\treturn st, err\n}\n\ntype valueFilterNext struct {\n\tsub    Scanner\n\tfilter ValueFilterFunc\n\tqs     refs.Namer\n\tresult refs.Ref\n\terr    error\n}\n\nfunc newValueFilterNext(qs refs.Namer, sub Scanner, filter ValueFilterFunc) *valueFilterNext {\n\treturn &valueFilterNext{\n\t\tsub:    sub,\n\t\tqs:     qs,\n\t\tfilter: filter,\n\t}\n}\n\nfunc (it *valueFilterNext) doFilter(val refs.Ref) bool {\n\tqval, err := it.qs.NameOf(val)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tok, err := it.filter(qval)\n\tif err != nil {\n\t\tit.err = err\n\t}\n\treturn ok\n}\n\nfunc (it *valueFilterNext) Close() error {\n\treturn it.sub.Close()\n}\n\nfunc (it *valueFilterNext) Next(ctx context.Context) bool {\n\tfor it.sub.Next(ctx) {\n\t\tval := it.sub.Result()\n\t\tif it.doFilter(val) {\n\t\t\tit.result = val\n\t\t\treturn true\n\t\t}\n\t}\n\tit.err = it.sub.Err()\n\treturn false\n}\n\nfunc (it *valueFilterNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *valueFilterNext) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *valueFilterNext) NextPath(ctx context.Context) bool {\n\treturn it.sub.NextPath(ctx)\n}\n\n// If we failed the check, then the subiterator should not contribute to the result\n// set. Otherwise, go ahead and tag it.\nfunc (it *valueFilterNext) TagResults(dst map[string]refs.Ref) {\n\tit.sub.TagResults(dst)\n}\n\nfunc (it *valueFilterNext) String() string {\n\treturn \"ValueFilterNext\"\n}\n\ntype valueFilterContains struct {\n\tsub    Index\n\tfilter ValueFilterFunc\n\tqs     refs.Namer\n\tresult refs.Ref\n\terr    error\n}\n\nfunc newValueFilterContains(qs refs.Namer, sub Index, filter ValueFilterFunc) *valueFilterContains {\n\treturn &valueFilterContains{\n\t\tsub:    sub,\n\t\tqs:     qs,\n\t\tfilter: filter,\n\t}\n}\n\nfunc (it *valueFilterContains) doFilter(val refs.Ref) bool {\n\tqval, err := it.qs.NameOf(val)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tok, err := it.filter(qval)\n\tif err != nil {\n\t\tit.err = err\n\t}\n\treturn ok\n}\n\nfunc (it *valueFilterContains) Close() error {\n\treturn it.sub.Close()\n}\n\nfunc (it *valueFilterContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *valueFilterContains) Result() refs.Ref {\n\treturn it.result\n}\n\nfunc (it *valueFilterContains) NextPath(ctx context.Context) bool {\n\treturn it.sub.NextPath(ctx)\n}\n\nfunc (it *valueFilterContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tif !it.doFilter(val) {\n\t\treturn false\n\t}\n\tok := it.sub.Contains(ctx, val)\n\tif !ok {\n\t\tit.err = it.sub.Err()\n\t}\n\treturn ok\n}\n\n// If we failed the check, then the subiterator should not contribute to the result\n// set. Otherwise, go ahead and tag it.\nfunc (it *valueFilterContains) TagResults(dst map[string]refs.Ref) {\n\tit.sub.TagResults(dst)\n}\n\nfunc (it *valueFilterContains) String() string {\n\treturn \"ValueFilterContains\"\n}\n"
  },
  {
    "path": "graph/kv/all/all.go",
    "content": "package all\n\nimport (\n\t// import all implementations that hidalgo supports\n\t_ \"github.com/hidal-go/hidalgo/kv/all\"\n\n\t// make sure to import kv package, so it can re-register hidalgo's backends\n\t_ \"github.com/cayleygraph/cayley/graph/kv\"\n\t// legacy: override bolt implementation; check the package for details\n\t_ \"github.com/cayleygraph/cayley/graph/kv/bolt\"\n)\n"
  },
  {
    "path": "graph/kv/all_iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kv\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/proto\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype constraint struct {\n\tdir quad.Direction\n\tval Int64Value\n}\n\ntype allIterator struct {\n\tqs    *QuadStore\n\tnodes bool\n\tcons  *constraint\n}\n\nfunc (qs *QuadStore) newAllIterator(nodes bool, cons *constraint) *allIterator {\n\tif nodes && cons != nil {\n\t\tpanic(\"cannot use a kv all iterator across nodes with a constraint\")\n\t}\n\treturn &allIterator{\n\t\tqs:    qs,\n\t\tnodes: nodes,\n\t\tcons:  cons,\n\t}\n}\n\nfunc (it *allIterator) Iterate() iterator.Scanner {\n\treturn it.qs.newAllIteratorNext(it.nodes, it.cons)\n}\n\nfunc (it *allIterator) Lookup() iterator.Index {\n\treturn it.qs.newAllIteratorContains(it.nodes, it.cons)\n}\n\n// No subiterators.\nfunc (it *allIterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *allIterator) String() string {\n\treturn \"KVAll\"\n}\n\nfunc (it *allIterator) Sorted() bool { return false }\n\nfunc (it *allIterator) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\treturn it, false\n}\n\nfunc (it *allIterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\treturn iterator.Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     2,\n\t\tSize: refs.Size{\n\t\t\tValue: it.qs.Size(),\n\t\t\tExact: false,\n\t\t},\n\t}, nil\n}\n\ntype allIteratorNext struct {\n\tnodes   bool\n\tid      uint64\n\tbuf     []*proto.Primitive\n\tprim    *proto.Primitive\n\thorizon int64\n\tqs      *QuadStore\n\terr     error\n\tcons    *constraint\n}\n\nfunc (qs *QuadStore) newAllIteratorNext(nodes bool, cons *constraint) *allIteratorNext {\n\tif nodes && cons != nil {\n\t\tpanic(\"cannot use a kv all iterator across nodes with a constraint\\n\")\n\t}\n\treturn &allIteratorNext{\n\t\tqs:      qs,\n\t\tnodes:   nodes,\n\t\thorizon: qs.horizon(context.TODO()),\n\t\tcons:    cons,\n\t}\n}\n\nfunc (it *allIteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *allIteratorNext) Close() error {\n\treturn nil\n}\n\nfunc (it *allIteratorNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *allIteratorNext) Result() graph.Ref {\n\tif it.id > uint64(it.horizon) {\n\t\treturn nil\n\t}\n\tif it.nodes {\n\t\treturn Int64Value(it.id)\n\t}\n\tif it.prim == nil {\n\t\treturn nil\n\t}\n\treturn it.prim\n}\n\nconst nextBatch = 100\n\nfunc (it *allIteratorNext) Next(ctx context.Context) bool {\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tfor {\n\t\tif len(it.buf) == 0 {\n\t\t\tif it.id+1 > uint64(it.horizon) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tids := make([]uint64, 0, nextBatch)\n\t\t\tfor i := 0; i < nextBatch; i++ {\n\t\t\t\tit.id++\n\t\t\t\tif it.id > uint64(it.horizon) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tids = append(ids, it.id)\n\t\t\t}\n\t\t\tif len(ids) == 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tit.buf, it.err = it.qs.getPrimitives(ctx, ids)\n\t\t\tif it.err != nil || len(it.buf) == 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t} else {\n\t\t\tit.buf = it.buf[1:]\n\t\t}\n\t\tfor ; len(it.buf) > 0; it.buf = it.buf[1:] {\n\t\t\tp := it.buf[0]\n\t\t\tit.prim = p\n\t\t\tif p == nil || p.Deleted {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tit.id = it.prim.ID\n\t\t\tif p.IsNode() && it.nodes {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif !p.IsNode() && !it.nodes {\n\t\t\t\tif it.cons == nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif Int64Value(p.GetDirection(it.cons.dir)) == it.cons.val {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (it *allIteratorNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *allIteratorNext) String() string {\n\treturn \"KVAllNext\"\n}\n\nfunc (it *allIteratorNext) Sorted() bool { return false }\n\ntype allIteratorContains struct {\n\tnodes   bool\n\tid      uint64\n\tprim    *proto.Primitive\n\thorizon int64\n\tqs      *QuadStore\n\terr     error\n\tcons    *constraint\n}\n\nfunc (qs *QuadStore) newAllIteratorContains(nodes bool, cons *constraint) *allIteratorContains {\n\tif nodes && cons != nil {\n\t\tpanic(\"cannot use a kv all iterator across nodes with a constraint\")\n\t}\n\treturn &allIteratorContains{\n\t\tqs:      qs,\n\t\tnodes:   nodes,\n\t\thorizon: qs.horizon(context.TODO()),\n\t\tcons:    cons,\n\t}\n}\n\nfunc (it *allIteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *allIteratorContains) Close() error {\n\treturn nil\n}\n\nfunc (it *allIteratorContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *allIteratorContains) Result() graph.Ref {\n\tif it.id > uint64(it.horizon) {\n\t\treturn nil\n\t}\n\tif it.nodes {\n\t\treturn Int64Value(it.id)\n\t}\n\tif it.prim == nil {\n\t\treturn nil\n\t}\n\treturn it.prim\n}\n\nfunc (it *allIteratorContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *allIteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\t// TODO(dennwc): This method doesn't check if the primitive still exists in the store.\n\t//               It's okay if we assume we provide the snapshot of data, though.\n\t//               However, passing a hand-crafted Ref will cause invalid results.\n\t//               Same is true for QuadIterator.\n\tif it.nodes {\n\t\tx, ok := v.(Int64Value)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tit.id = uint64(x)\n\t\treturn it.id <= uint64(it.horizon)\n\t}\n\tp, ok := v.(*proto.Primitive)\n\tif !ok {\n\t\treturn false\n\t}\n\tit.prim = p\n\tit.id = it.prim.ID\n\tif it.cons == nil {\n\t\treturn true\n\t}\n\tif Int64Value(it.prim.GetDirection(it.cons.dir)) != it.cons.val {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (it *allIteratorContains) String() string {\n\treturn \"KVAllContains\"\n}\n\nfunc (it *allIteratorContains) Sorted() bool { return false }\n"
  },
  {
    "path": "graph/kv/badger/badger.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage badger\n\nimport (\n\t\"os\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/flat\"\n\t\"github.com/hidal-go/hidalgo/kv/flat/badger\"\n)\n\nconst (\n\tType = badger.Name\n)\n\nfunc Create(path string, m graph.Options) (hkv.KV, error) {\n\tif path == \"\" {\n\t\treturn nil, kv.ErrEmptyPath\n\t}\n\terr := os.MkdirAll(path, 0700)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdb, err := badger.OpenPath(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn flat.Upgrade(db), nil\n}\n"
  },
  {
    "path": "graph/kv/badger/badger_test.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage badger\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv/kvtest\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n)\n\nfunc makeBadgerkv(t testing.TB) (hkv.KV, graph.Options, func()) {\n\ttmpDir, err := ioutil.TempDir(os.TempDir(), \"cayley_test_\"+Type)\n\tif err != nil {\n\t\tt.Fatalf(\"Could not create working directory: %v\", err)\n\t}\n\tdb, err := Create(tmpDir, nil)\n\tif err != nil {\n\t\tos.RemoveAll(tmpDir)\n\t\tt.Fatal(\"Failed to create Badger database.\", err)\n\t}\n\treturn db, nil, func() {\n\t\tdb.Close()\n\t\tos.RemoveAll(tmpDir)\n\t}\n}\n\nfunc TestBadgerkv(t *testing.T) {\n\tkvtest.TestAll(t, makeBadgerkv, nil)\n}\n\nfunc BenchmarkBadgerkv(b *testing.B) {\n\tkvtest.BenchmarkAll(b, makeBadgerkv, nil)\n}\n"
  },
  {
    "path": "graph/kv/bbolt/bolt.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage bbolt\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\tbolt \"github.com/hidal-go/hidalgo/kv/bbolt\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv\"\n)\n\nfunc init() {\n\t// override implementation; hidalgo expects a path to a database file,\n\t// while cayley was using path/index.bolt file previously\n\tkv.Register(Type, kv.Registration{\n\t\tNewFunc:      Open,\n\t\tInitFunc:     Create,\n\t\tIsPersistent: true,\n\t})\n}\n\nconst (\n\tType = bolt.Name\n)\n\nfunc getBoltFile(cfgpath string) string {\n\treturn filepath.Join(cfgpath, \"indexes.bolt\")\n}\n\nfunc Create(path string, _ graph.Options) (hkv.KV, error) {\n\tif path == \"\" {\n\t\treturn nil, kv.ErrEmptyPath\n\t}\n\terr := os.MkdirAll(path, 0700)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdb, err := bolt.Open(getBoltFile(path), nil)\n\tif err != nil {\n\t\tclog.Errorf(\"Error: couldn't create Bolt database: %v\", err)\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc Open(path string, opt graph.Options) (hkv.KV, error) {\n\tdb, err := bolt.Open(getBoltFile(path), nil)\n\tif err != nil {\n\t\tclog.Errorf(\"Error, couldn't open! %v\", err)\n\t\treturn nil, err\n\t}\n\tbdb := db.DB()\n\t// BoolKey returns false on non-existence. IE, Sync by default.\n\tbdb.NoSync, err = opt.BoolKey(\"nosync\", false)\n\tif err != nil {\n\t\tdb.Close()\n\t\treturn nil, err\n\t}\n\tbdb.NoGrowSync = bdb.NoSync\n\tif bdb.NoSync {\n\t\tclog.Infof(\"Running in nosync mode\")\n\t}\n\treturn db, nil\n}\n"
  },
  {
    "path": "graph/kv/bbolt/bolt_test.go",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage bbolt\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv/kvtest\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n)\n\nfunc makeBolt(t testing.TB) (hkv.KV, graph.Options, func()) {\n\ttmpDir, err := ioutil.TempDir(os.TempDir(), \"cayley_test_\"+Type)\n\tif err != nil {\n\t\tt.Fatalf(\"Could not create working directory: %v\", err)\n\t}\n\tdb, err := Create(tmpDir, nil)\n\tif err != nil {\n\t\tos.RemoveAll(tmpDir)\n\t\tt.Fatal(\"Failed to create Bolt database.\", err)\n\t}\n\treturn db, nil, func() {\n\t\tdb.Close()\n\t\tos.RemoveAll(tmpDir)\n\t}\n}\n\nfunc TestBolt(t *testing.T) {\n\tkvtest.TestAll(t, makeBolt, nil)\n}\n\nfunc BenchmarkBolt(b *testing.B) {\n\tkvtest.BenchmarkAll(b, makeBolt, nil)\n}\n"
  },
  {
    "path": "graph/kv/bolt/bolt.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage bolt\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/bolt\"\n)\n\nfunc init() {\n\t// override implementation; hidalgo expects a path to a database file,\n\t// while cayley was using path/index.bolt file previously\n\tkv.Register(Type, kv.Registration{\n\t\tNewFunc:      Open,\n\t\tInitFunc:     Create,\n\t\tIsPersistent: true,\n\t})\n}\n\nconst (\n\tType = bolt.Name\n)\n\nfunc getBoltFile(cfgpath string) string {\n\treturn filepath.Join(cfgpath, \"indexes.bolt\")\n}\n\nfunc Create(path string, _ graph.Options) (hkv.KV, error) {\n\tif path == \"\" {\n\t\treturn nil, kv.ErrEmptyPath\n\t}\n\terr := os.MkdirAll(path, 0700)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdb, err := bolt.Open(getBoltFile(path), nil)\n\tif err != nil {\n\t\tclog.Errorf(\"Error: couldn't create Bolt database: %v\", err)\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc Open(path string, opt graph.Options) (hkv.KV, error) {\n\tdb, err := bolt.Open(getBoltFile(path), nil)\n\tif err != nil {\n\t\tclog.Errorf(\"Error, couldn't open! %v\", err)\n\t\treturn nil, err\n\t}\n\tbdb := db.DB()\n\t// BoolKey returns false on non-existence. IE, Sync by default.\n\tbdb.NoSync, err = opt.BoolKey(\"nosync\", false)\n\tif err != nil {\n\t\tdb.Close()\n\t\treturn nil, err\n\t}\n\tbdb.NoGrowSync = bdb.NoSync\n\tif bdb.NoSync {\n\t\tclog.Infof(\"Running in nosync mode\")\n\t}\n\treturn db, nil\n}\n"
  },
  {
    "path": "graph/kv/bolt/bolt_test.go",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage bolt\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv/kvtest\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n)\n\nfunc makeBolt(t testing.TB) (hkv.KV, graph.Options, func()) {\n\ttmpDir, err := ioutil.TempDir(os.TempDir(), \"cayley_test_\"+Type)\n\tif err != nil {\n\t\tt.Fatalf(\"Could not create working directory: %v\", err)\n\t}\n\tdb, err := Create(tmpDir, nil)\n\tif err != nil {\n\t\tos.RemoveAll(tmpDir)\n\t\tt.Fatal(\"Failed to create Bolt database.\", err)\n\t}\n\treturn db, nil, func() {\n\t\tdb.Close()\n\t\tos.RemoveAll(tmpDir)\n\t}\n}\n\nfunc TestBolt(t *testing.T) {\n\tkvtest.TestAll(t, makeBolt, nil)\n}\n\nfunc BenchmarkBolt(b *testing.B) {\n\tkvtest.BenchmarkAll(b, makeBolt, nil)\n}\n"
  },
  {
    "path": "graph/kv/btree/btree.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage btree\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/flat\"\n\t\"github.com/hidal-go/hidalgo/kv/flat/btree\"\n)\n\nconst (\n\tType = btree.Name\n)\n\nfunc Create(path string, _ graph.Options) (hkv.KV, error) {\n\treturn New(), nil\n}\n\nfunc New() hkv.KV {\n\treturn flat.Upgrade(btree.New())\n}\n"
  },
  {
    "path": "graph/kv/btree/btree_test.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage btree\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv/kvtest\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/kvdebug\"\n)\n\nconst debug = false\n\nfunc makeBtree(t testing.TB) (hkv.KV, graph.Options, func()) {\n\tif debug {\n\t\treturn makeBtreeDebug(t)\n\t}\n\treturn New(), nil, func() {}\n}\n\nfunc makeBtreeDebug(t testing.TB) (hkv.KV, graph.Options, func()) {\n\tdb := New()\n\td := kvdebug.New(db)\n\td.Log(true)\n\treturn d, nil, func() {\n\t\td.Close()\n\t\tt.Logf(\"kv stats: %+v\", d.Stats())\n\t}\n}\n\nvar conf = &kvtest.Config{\n\tAlwaysRunIntegration: true,\n}\n\nfunc TestBtree(t *testing.T) {\n\tkvtest.TestAll(t, makeBtree, conf)\n}\n\nfunc BenchmarkBtree(b *testing.B) {\n\tkvtest.BenchmarkAll(b, makeBtree, conf)\n}\n"
  },
  {
    "path": "graph/kv/indexing.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kv\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n\t\"github.com/hidal-go/hidalgo/kv/options\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n\tcproto \"github.com/cayleygraph/cayley/graph/proto\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\n\t\"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\tboom \"github.com/tylertreat/BoomFilters\"\n)\n\nvar (\n\tmetaBucket = kv.Key{[]byte(\"meta\")}\n\tlogIndex   = kv.Key{[]byte(\"log\")}\n\n\tkeyMetaIndexes = metaBucket.AppendBytes([]byte(\"indexes\"))\n\n\t// List of all buckets in the current version of the database.\n\tbuckets = []kv.Key{\n\t\tmetaBucket,\n\t\tlogIndex,\n\t}\n\n\t// legacyQuadIndexes is a set of indexes used in Cayley < 0.7.6\n\tlegacyQuadIndexes = []QuadIndex{\n\t\t{Dirs: []quad.Direction{quad.Subject}},\n\t\t{Dirs: []quad.Direction{quad.Object}},\n\t}\n\n\tDefaultQuadIndexes = []QuadIndex{\n\t\t// First index optimizes forward traversals. Getting all relations for a node should\n\t\t// also be reasonably fast (prefix scan).\n\t\t{Dirs: []quad.Direction{quad.Subject, quad.Predicate}},\n\n\t\t// Second index helps with reverse traversals as well as full quad lookups.\n\t\t// It also prevents issues with super-nodes, since most of those are values\n\t\t// with a high in-degree.\n\t\t{Dirs: []quad.Direction{quad.Object, quad.Predicate, quad.Subject}},\n\t}\n)\n\nvar quadKeyEnc = binary.BigEndian\n\ntype QuadIndex struct {\n\tDirs   []quad.Direction `json:\"dirs\"`\n\tUnique bool             `json:\"unique\"`\n}\n\nfunc (ind QuadIndex) Key(vals []uint64) kv.Key {\n\tkey := make([]byte, 8*len(vals))\n\tn := 0\n\tfor i := range vals {\n\t\tquadKeyEnc.PutUint64(key[n:], vals[i])\n\t\tn += 8\n\t}\n\t// TODO(dennwc): split into parts?\n\treturn ind.bucket().AppendBytes(key)\n}\nfunc (ind QuadIndex) KeyFor(p *cproto.Primitive) kv.Key {\n\tkey := make([]byte, 8*len(ind.Dirs))\n\tn := 0\n\tfor _, d := range ind.Dirs {\n\t\tquadKeyEnc.PutUint64(key[n:], p.GetDirection(d))\n\t\tn += 8\n\t}\n\t// TODO(dennwc): split into parts?\n\treturn ind.bucket().AppendBytes(key)\n}\nfunc (ind QuadIndex) bucket() kv.Key {\n\tbuf := make([]byte, len(ind.Dirs))\n\tfor i, d := range ind.Dirs {\n\t\tbuf[i] = d.Prefix()\n\t}\n\tkey := make(kv.Key, 1, 2)\n\tkey[0] = buf\n\treturn key\n}\n\nfunc bucketForVal(i, j byte) kv.Key {\n\treturn kv.Key{[]byte{'v', i, j}}\n}\n\nfunc bucketForValRefs(i, j byte) kv.Key {\n\treturn kv.Key{[]byte{'n', i, j}}\n}\n\nfunc (qs *QuadStore) createBuckets(ctx context.Context, upfront bool) error {\n\terr := kv.Update(ctx, qs.db, func(tx kv.Tx) error {\n\t\tfor _, index := range buckets {\n\t\t\t_ = kv.CreateBucket(ctx, tx, index)\n\t\t}\n\t\tfor _, ind := range qs.indexes.all {\n\t\t\t_ = kv.CreateBucket(ctx, tx, ind.bucket())\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !upfront {\n\t\treturn nil\n\t}\n\tfor i := 0; i < 256; i++ {\n\t\terr := kv.Update(ctx, qs.db, func(tx kv.Tx) error {\n\t\t\tfor j := 0; j < 256; j++ {\n\t\t\t\t_ = kv.CreateBucket(ctx, tx, bucketForVal(byte(i), byte(j)))\n\t\t\t\t_ = kv.CreateBucket(ctx, tx, bucketForValRefs(byte(i), byte(j)))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) incSize(ctx context.Context, tx kv.Tx, size int64) error {\n\t_, err := qs.incMetaInt(ctx, tx, \"size\", size)\n\treturn err\n}\n\n// writeIndexesMeta writes metadata about current indexes to the KV database,\n// so we can read this information back later.\nfunc (qs *QuadStore) writeIndexesMeta(ctx context.Context) error {\n\t// TODO(dennwc): change to protobuf later?\n\tdata, err := json.Marshal(qs.indexes.all)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn kv.Update(ctx, qs.db, func(tx kv.Tx) error {\n\t\treturn tx.Put(ctx, keyMetaIndexes, data)\n\t})\n}\n\n// readIndexesMeta read metadata about current indexes from the KV database.\n// If no indexes are set, it returns a list of legacy indexes to preserve backward compatibility.\nfunc (qs *QuadStore) readIndexesMeta(ctx context.Context) ([]QuadIndex, error) {\n\ttx, err := qs.db.Tx(ctx, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer tx.Close()\n\ttx = wrapTx(tx)\n\tval, err := tx.Get(ctx, keyMetaIndexes)\n\tif err == kv.ErrNotFound {\n\t\treturn legacyQuadIndexes, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\tvar out []QuadIndex\n\tif err := json.Unmarshal(val, &out); err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot decode indexes: %v\", err)\n\t} else if len(out) == 0 {\n\t\treturn legacyQuadIndexes, nil\n\t}\n\treturn out, nil\n}\n\nfunc (qs *QuadStore) resolveValDeltas(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate, fnc func(i int, id uint64)) error {\n\tinds := make([]int, 0, len(deltas))\n\tkeys := make([]kv.Key, 0, len(deltas))\n\tfor i, d := range deltas {\n\t\tif iri, ok := d.Val.(quad.IRI); ok {\n\t\t\tif x, ok := qs.valueLRU.Get(string(iri)); ok {\n\t\t\t\tfnc(i, x.(uint64))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else if d.Val == nil {\n\t\t\tfnc(i, 0)\n\t\t\tcontinue\n\t\t}\n\t\tif qs.mapNodes != nil && !qs.mapNodes.Test(d.Hash[:]) {\n\t\t\tfnc(i, 0)\n\t\t\tcontinue\n\t\t}\n\t\tinds = append(inds, i)\n\t\tkeys = append(keys, bucketKeyForHash(d.Hash))\n\t}\n\tif len(keys) == 0 {\n\t\treturn nil\n\t}\n\tresp, err := tx.GetBatch(ctx, keys)\n\tif err != nil {\n\t\treturn err\n\t}\n\tkeys = nil\n\tfor i, b := range resp {\n\t\tif len(b) == 0 {\n\t\t\tfnc(inds[i], 0)\n\t\t\tcontinue\n\t\t}\n\t\tind := inds[i]\n\t\tid, _ := binary.Uvarint(b)\n\t\td := &deltas[ind]\n\t\tif iri, ok := d.Val.(quad.IRI); ok && id != 0 {\n\t\t\tqs.valueLRU.Put(string(iri), uint64(id))\n\t\t}\n\t\tfnc(ind, uint64(id))\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) getMetaIntTx(ctx context.Context, tx kv.Tx, key string) (int64, error) {\n\tval, err := tx.Get(ctx, metaBucket.AppendBytes([]byte(key)))\n\tif err == kv.ErrNotFound {\n\t\treturn 0, err\n\t} else if err != nil {\n\t\treturn 0, fmt.Errorf(\"cannot get horizon value: %v\", err)\n\t}\n\treturn int64(binary.LittleEndian.Uint64(val)), nil\n}\n\nfunc (qs *QuadStore) incMetaInt(ctx context.Context, tx kv.Tx, key string, n int64) (int64, error) {\n\tif n == 0 {\n\t\treturn 0, nil\n\t}\n\tv, err := qs.getMetaIntTx(ctx, tx, key)\n\tif err != nil && err != kv.ErrNotFound {\n\t\treturn 0, fmt.Errorf(\"cannot get %s: %v\", key, err)\n\t}\n\tstart := v\n\tv += n\n\n\tbuf := make([]byte, 8) // bolt needs all slices available on Commit\n\tbinary.LittleEndian.PutUint64(buf, uint64(v))\n\n\terr = tx.Put(ctx, metaBucket.AppendBytes([]byte(key)), buf)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"cannot inc %s: %v\", key, err)\n\t}\n\treturn start, nil\n}\n\nfunc (qs *QuadStore) genIDs(ctx context.Context, tx kv.Tx, n int) (uint64, error) {\n\tif n == 0 {\n\t\treturn 0, nil\n\t}\n\tstart, err := qs.incMetaInt(ctx, tx, \"horizon\", int64(n))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn uint64(start + 1), nil\n}\n\ntype nodeUpdate struct {\n\tInd int\n\tID  uint64\n\tgraphlog.NodeUpdate\n}\n\nfunc (qs *QuadStore) incNodesCnt(ctx context.Context, tx kv.Tx, deltas, newDeltas []nodeUpdate) ([]int, error) {\n\tvar buf [binary.MaxVarintLen64]byte\n\t// increment nodes\n\tkeys := make([]kv.Key, 0, len(deltas))\n\tfor _, d := range deltas {\n\t\tkeys = append(keys, bucketKeyForHashRefs(d.Hash))\n\t}\n\tsizes, err := tx.GetBatch(ctx, keys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar del []int\n\tfor i, d := range deltas {\n\t\tk := keys[i]\n\t\tvar sz int64\n\t\tif sizes[i] != nil {\n\t\t\tszu, _ := binary.Uvarint(sizes[i])\n\t\t\tsz = int64(szu)\n\t\t\tsizes[i] = nil // cannot reuse buffer since it belongs to kv\n\t\t}\n\t\tsz += int64(d.RefInc)\n\t\tif sz <= 0 {\n\t\t\tif err := tx.Del(ctx, k); err != nil {\n\t\t\t\treturn del, err\n\t\t\t}\n\t\t\tmNodesDel.Inc()\n\t\t\tdel = append(del, i)\n\t\t\tcontinue\n\t\t}\n\t\tn := binary.PutUvarint(buf[:], uint64(sz))\n\t\tval := append([]byte{}, buf[:n]...)\n\t\tif err := tx.Put(ctx, k, val); err != nil {\n\t\t\treturn del, err\n\t\t}\n\t\tmNodesUpd.Inc()\n\t}\n\t// create new nodes\n\tfor _, d := range newDeltas {\n\t\tn := binary.PutUvarint(buf[:], uint64(d.RefInc))\n\t\tval := append([]byte{}, buf[:n]...)\n\t\tif err := tx.Put(ctx, bucketKeyForHashRefs(d.Hash), val); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tmNodesNew.Inc()\n\t}\n\treturn del, nil\n}\n\ntype resolvedNode struct {\n\tID  uint64\n\tNew bool\n}\n\nfunc (qs *QuadStore) incNodes(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate) (map[refs.ValueHash]resolvedNode, error) {\n\tvar (\n\t\tins []nodeUpdate\n\t\tupd = make([]nodeUpdate, 0, len(deltas))\n\t\tids = make(map[refs.ValueHash]resolvedNode, len(deltas))\n\t)\n\terr := qs.resolveValDeltas(ctx, tx, deltas, func(i int, id uint64) {\n\t\tif id == 0 {\n\t\t\t// not exists, should create\n\t\t\tins = append(ins, nodeUpdate{Ind: i, NodeUpdate: deltas[i]})\n\t\t} else {\n\t\t\t// exists, should update\n\t\t\tupd = append(upd, nodeUpdate{Ind: i, ID: id, NodeUpdate: deltas[i]})\n\t\t\tids[deltas[i].Hash] = resolvedNode{ID: id}\n\t\t}\n\t})\n\tif err != nil {\n\t\treturn ids, err\n\t}\n\tif len(ins) != 0 {\n\t\t// preallocate IDs\n\t\tstart, err := qs.genIDs(ctx, tx, len(ins))\n\t\tif err != nil {\n\t\t\treturn ids, err\n\t\t}\n\t\t// create and index new nodes\n\t\tfor i, iv := range ins {\n\t\t\tid := start + uint64(i)\n\t\t\tnode, err := createNodePrimitive(iv.Val)\n\t\t\tif err != nil {\n\t\t\t\treturn ids, err\n\t\t\t}\n\n\t\t\tnode.ID = id\n\t\t\tids[iv.Hash] = resolvedNode{ID: id, New: true}\n\t\t\tif err := qs.indexNode(ctx, tx, node, iv.Val); err != nil {\n\t\t\t\treturn ids, err\n\t\t\t}\n\t\t\tins[i].ID = id\n\t\t}\n\t}\n\t_, err = qs.incNodesCnt(ctx, tx, upd, ins)\n\treturn ids, err\n}\nfunc (qs *QuadStore) decNodes(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate, nodes map[refs.ValueHash]uint64) error {\n\tupds := make([]nodeUpdate, 0, len(deltas))\n\tfor i, d := range deltas {\n\t\tid := nodes[d.Hash]\n\t\tif id == 0 || d.RefInc == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tupds = append(upds, nodeUpdate{Ind: i, ID: id, NodeUpdate: d})\n\t}\n\tdel, err := qs.incNodesCnt(ctx, tx, upds, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, i := range del {\n\t\td := upds[i]\n\t\tkey := bucketForVal(d.Hash[0], d.Hash[1]).AppendBytes(d.Hash[:])\n\t\tif err = tx.Del(ctx, key); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif iri, ok := d.Val.(quad.IRI); ok {\n\t\t\tqs.valueLRU.Del(string(iri))\n\t\t}\n\t\tif err := qs.delLog(ctx, tx, d.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn &quadWriter{qs: qs}, nil\n}\n\ntype quadWriter struct {\n\tqs  *QuadStore\n\ttx  kv.Tx\n\terr error\n\tn   int\n}\n\nfunc (w *quadWriter) WriteQuad(q quad.Quad) error {\n\t_, err := w.WriteQuads([]quad.Quad{q})\n\treturn err\n}\n\nfunc (w *quadWriter) flush(ctx context.Context) error {\n\tw.n = 0\n\tif err := w.qs.flushMapBucket(ctx, w.tx); err != nil {\n\t\tw.err = err\n\t\treturn err\n\t}\n\tif err := w.tx.Commit(ctx); err != nil {\n\t\tw.qs.writer.Unlock()\n\t\tw.tx = nil\n\t\tw.err = err\n\t\treturn err\n\t}\n\ttx, err := w.qs.db.Tx(ctx, true)\n\tif err != nil {\n\t\tw.qs.writer.Unlock()\n\t\tw.err = err\n\t\treturn err\n\t}\n\tw.tx = wrapTx(tx)\n\treturn nil\n}\n\nfunc (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\tmApplyBatch.Observe(float64(len(buf)))\n\tdefer prometheus.NewTimer(mApplySeconds).ObserveDuration()\n\n\tctx := context.TODO()\n\tif w.tx == nil {\n\t\tw.qs.writer.Lock()\n\t\ttx, err := w.qs.db.Tx(ctx, true)\n\t\tif err != nil {\n\t\t\tw.qs.writer.Unlock()\n\t\t\tw.err = err\n\t\t\treturn 0, err\n\t\t}\n\t\tw.tx = wrapTx(tx)\n\t}\n\tdeltas := graphlog.InsertQuads(buf)\n\tif _, err := w.qs.applyAddDeltas(ctx, w.tx, nil, deltas, graph.IgnoreOpts{IgnoreDup: true}); err != nil {\n\t\tw.err = err\n\t\treturn 0, err\n\t}\n\tw.n += len(buf)\n\tif w.n >= quad.DefaultBatch*20 {\n\t\tif err := w.flush(ctx); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\tif w.tx == nil {\n\t\treturn w.err\n\t}\n\tdefer w.qs.writer.Unlock()\n\n\tif w.err != nil {\n\t\t_ = w.tx.Close()\n\t\tw.tx = nil\n\t\treturn w.err\n\t}\n\n\tctx := context.TODO()\n\t// flush quad indexes and commit\n\terr := w.qs.flushMapBucket(ctx, w.tx)\n\tif err != nil {\n\t\t_ = w.tx.Close()\n\t\tw.tx = nil\n\t\treturn err\n\t}\n\terr = w.tx.Commit(ctx)\n\tw.tx = nil\n\treturn err\n}\n\nfunc (qs *QuadStore) applyAddDeltas(ctx context.Context, tx kv.Tx, in []graph.Delta, deltas *graphlog.Deltas, ignoreOpts graph.IgnoreOpts) (map[refs.ValueHash]resolvedNode, error) {\n\t// first add all new nodes\n\tnodes, err := qs.incNodes(ctx, tx, deltas.IncNode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdeltas.IncNode = nil\n\t// resolve and insert all new quads\n\tlinks := make([]*cproto.Primitive, 0, len(deltas.QuadAdd))\n\tqadd := make(map[[4]uint64]struct{}, len(deltas.QuadAdd))\n\tfor _, q := range deltas.QuadAdd {\n\t\tvar link cproto.Primitive\n\t\tmustBeNew := false\n\t\tvar qkey [4]uint64\n\t\tfor i, dir := range quad.Directions {\n\t\t\tn, ok := nodes[q.Quad.Get(dir)]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmustBeNew = mustBeNew || n.New\n\t\t\tlink.SetDirection(dir, n.ID)\n\t\t\tqkey[i] = n.ID\n\t\t}\n\t\tif _, ok := qadd[qkey]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tqadd[qkey] = struct{}{}\n\t\tif !mustBeNew {\n\t\t\tp, err := qs.hasPrimitive(ctx, tx, &link, false)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif p != nil {\n\t\t\t\tif ignoreOpts.IgnoreDup {\n\t\t\t\t\tcontinue // already exists, no need to insert\n\t\t\t\t}\n\t\t\t\terr = graph.ErrQuadExists\n\t\t\t\tif len(in) != 0 {\n\t\t\t\t\treturn nil, &graph.DeltaError{Delta: in[q.Ind], Err: err}\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tlinks = append(links, &link)\n\t}\n\tqadd = nil\n\tdeltas.QuadAdd = nil\n\n\tqstart, err := qs.genIDs(ctx, tx, len(links))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor i := range links {\n\t\tlinks[i].ID = qstart + uint64(i)\n\t\tlinks[i].Timestamp = time.Now().UnixNano()\n\t}\n\tif err := qs.indexLinks(ctx, tx, links); err != nil {\n\t\treturn nil, err\n\t}\n\treturn nodes, nil\n}\n\nfunc (qs *QuadStore) ApplyDeltas(in []graph.Delta, ignoreOpts graph.IgnoreOpts) error {\n\tmApplyBatch.Observe(float64(len(in)))\n\tdefer prometheus.NewTimer(mApplySeconds).ObserveDuration()\n\n\tctx := context.TODO()\n\tqs.writer.Lock()\n\tdefer qs.writer.Unlock()\n\ttx, err := qs.db.Tx(ctx, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer tx.Close()\n\ttx = wrapTx(tx)\n\n\tdeltas := graphlog.SplitDeltas(in)\n\tif len(deltas.QuadDel) != 0 || len(deltas.DecNode) != 0 {\n\t\tqs.mapNodes = nil\n\t}\n\n\tnodes, err := qs.applyAddDeltas(ctx, tx, in, deltas, ignoreOpts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(deltas.QuadDel) != 0 || len(deltas.DecNode) != 0 {\n\t\tlinks := make([]*cproto.Primitive, 0, len(deltas.QuadDel))\n\t\t// resolve all nodes that will be removed\n\t\tdnodes := make(map[refs.ValueHash]uint64, len(deltas.DecNode))\n\t\tif err := qs.resolveValDeltas(ctx, tx, deltas.DecNode, func(i int, id uint64) {\n\t\t\tdnodes[deltas.DecNode[i].Hash] = id\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// check for existence and delete quads\n\t\tfixNodes := make(map[refs.ValueHash]int)\n\t\tfor _, q := range deltas.QuadDel {\n\t\t\tlink := new(cproto.Primitive)\n\t\t\texists := true\n\t\t\t// resolve values of all quad directions\n\t\t\t// if any of the direction does not exists, the quad does not exists as well\n\t\t\tfor _, dir := range quad.Directions {\n\t\t\t\th := q.Quad.Get(dir)\n\t\t\t\tn, ok := nodes[h]\n\t\t\t\tif !ok {\n\t\t\t\t\tvar id uint64\n\t\t\t\t\tid, ok = dnodes[h]\n\t\t\t\t\tn.ID = id\n\t\t\t\t}\n\t\t\t\tif !ok {\n\t\t\t\t\texists = exists && !h.Valid()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlink.SetDirection(dir, n.ID)\n\t\t\t}\n\t\t\tif exists {\n\t\t\t\tp, err := qs.hasPrimitive(ctx, tx, link, true)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t} else if p == nil || p.Deleted {\n\t\t\t\t\texists = false\n\t\t\t\t} else {\n\t\t\t\t\tlink = p\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\tif !ignoreOpts.IgnoreMissing {\n\t\t\t\t\treturn &graph.DeltaError{Delta: in[q.Ind], Err: graph.ErrQuadNotExist}\n\t\t\t\t}\n\t\t\t\t// revert counters for all directions of this quad\n\t\t\t\tfor _, dir := range quad.Directions {\n\t\t\t\t\tif h := q.Quad.Get(dir); h.Valid() {\n\t\t\t\t\t\tfixNodes[h]++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlinks = append(links, link)\n\t\t}\n\t\tdeltas.QuadDel = nil\n\t\tif err := qs.markLinksDead(ctx, tx, links); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlinks = nil\n\t\tnodes = nil\n\n\t\t// we decremented some nodes that has non-existent quads - let's fix this\n\t\tif len(fixNodes) != 0 {\n\t\t\tfor i, n := range deltas.DecNode {\n\t\t\t\tif dn := fixNodes[n.Hash]; dn != 0 {\n\t\t\t\t\tdeltas.DecNode[i].RefInc += dn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// finally decrement and remove nodes\n\t\tif err := qs.decNodes(ctx, tx, deltas.DecNode, dnodes); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdeltas = nil\n\t\tdnodes = nil\n\t}\n\t// flush quad indexes and commit\n\terr = qs.flushMapBucket(ctx, tx)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn tx.Commit(ctx)\n}\n\nfunc (qs *QuadStore) indexNode(ctx context.Context, tx kv.Tx, p *cproto.Primitive, val quad.Value) error {\n\tvar err error\n\tif val == nil {\n\t\tval, err = pquads.UnmarshalValue(p.Value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\thash := quad.HashOf(val)\n\terr = tx.Put(ctx, bucketForVal(hash[0], hash[1]).AppendBytes(hash), uint64toBytes(p.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif iri, ok := val.(quad.IRI); ok {\n\t\tqs.valueLRU.Put(string(iri), p.ID)\n\t}\n\tif qs.mapNodes != nil {\n\t\tqs.mapNodes.Add(hash)\n\t}\n\treturn qs.addToLog(ctx, tx, p)\n}\n\nfunc (qs *QuadStore) indexLinks(ctx context.Context, tx kv.Tx, links []*cproto.Primitive) error {\n\tfor _, p := range links {\n\t\tif err := qs.indexLink(ctx, tx, p); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn qs.incSize(ctx, tx, int64(len(links)))\n}\nfunc (qs *QuadStore) indexLink(ctx context.Context, tx kv.Tx, p *cproto.Primitive) error {\n\tvar err error\n\tqs.indexes.RLock()\n\tall := qs.indexes.all\n\tqs.indexes.RUnlock()\n\tfor _, ind := range all {\n\t\terr = qs.addToMapBucket(tx, ind.KeyFor(p), p.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tqs.bloomAdd(p)\n\terr = qs.indexSchema(tx, p)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn qs.addToLog(ctx, tx, p)\n}\n\nfunc (qs *QuadStore) markAsDead(ctx context.Context, tx kv.Tx, p *cproto.Primitive) error {\n\tp.Deleted = true\n\t//TODO(barakmich): Add tombstone?\n\tqs.bloomRemove(p)\n\treturn qs.addToLog(ctx, tx, p)\n}\n\nfunc (qs *QuadStore) delLog(ctx context.Context, tx kv.Tx, id uint64) error {\n\treturn tx.Del(ctx, logIndex.Append(uint64KeyBytes(id)))\n}\n\nfunc (qs *QuadStore) markLinksDead(ctx context.Context, tx kv.Tx, links []*cproto.Primitive) error {\n\tfor _, p := range links {\n\t\tif err := qs.markAsDead(ctx, tx, p); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn qs.incSize(ctx, tx, -int64(len(links)))\n}\n\nfunc (qs *QuadStore) getBucketIndexes(ctx context.Context, tx kv.Tx, keys []kv.Key) ([][]uint64, error) {\n\tvals, err := tx.GetBatch(ctx, keys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tout := make([][]uint64, len(keys))\n\tfor i, v := range vals {\n\t\tif len(v) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tind, err := decodeIndex(v)\n\t\tif err != nil {\n\t\t\treturn out, err\n\t\t}\n\t\tout[i] = ind\n\t}\n\treturn out, nil\n}\n\nfunc countIndex(b []byte) (int64, error) {\n\tvar cnt int64\n\tfor len(b) > 0 {\n\t\t_, n := binary.Uvarint(b)\n\t\tif n == 0 {\n\t\t\treturn 0, io.ErrUnexpectedEOF\n\t\t} else if n < 0 {\n\t\t\treturn 0, errors.New(\"varint: overflow\")\n\t\t}\n\t\tcnt++\n\t\tb = b[n:]\n\t}\n\treturn cnt, nil\n}\n\nfunc decodeIndex(b []byte) ([]uint64, error) {\n\tvar out []uint64\n\tfor len(b) > 0 {\n\t\tv, n := binary.Uvarint(b)\n\t\tif n == 0 {\n\t\t\treturn out, io.ErrUnexpectedEOF\n\t\t} else if n < 0 {\n\t\t\treturn out, errors.New(\"varint: overflow\")\n\t\t}\n\t\tout = append(out, v)\n\t\tb = b[n:]\n\t}\n\treturn out, nil\n}\n\nfunc appendIndex(bytelist []byte, l []uint64) []byte {\n\tb := make([]byte, len(bytelist)+(binary.MaxVarintLen64*len(l)))\n\tcopy(b[:len(bytelist)], bytelist)\n\toff := len(bytelist)\n\tfor _, x := range l {\n\t\tn := binary.PutUvarint(b[off:], x)\n\t\toff += n\n\t}\n\treturn b[:off]\n}\n\nfunc (qs *QuadStore) bestUnique() ([]QuadIndex, error) {\n\tqs.indexes.RLock()\n\tind := qs.indexes.exists\n\tqs.indexes.RUnlock()\n\tif len(ind) != 0 {\n\t\treturn ind, nil\n\t}\n\tqs.indexes.Lock()\n\tdefer qs.indexes.Unlock()\n\tif len(qs.indexes.exists) != 0 {\n\t\treturn qs.indexes.exists, nil\n\t}\n\tfor _, in := range qs.indexes.all {\n\t\tif in.Unique {\n\t\t\tif clog.V(2) {\n\t\t\t\tclog.Infof(\"using unique index: %v\", in.Dirs)\n\t\t\t}\n\t\t\tqs.indexes.exists = []QuadIndex{in}\n\t\t\treturn qs.indexes.exists, nil\n\t\t}\n\t}\n\t// TODO: find best combination of indexes\n\tinds := qs.indexes.all\n\tif len(inds) == 0 {\n\t\treturn nil, fmt.Errorf(\"no indexes defined\")\n\t}\n\tif clog.V(2) {\n\t\tclog.Infof(\"using index intersection: %v\", inds)\n\t}\n\tqs.indexes.exists = inds\n\treturn qs.indexes.exists, nil\n}\n\nfunc hasDir(dirs []quad.Direction, d quad.Direction) bool {\n\tfor _, d2 := range dirs {\n\t\tif d == d2 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (qs *QuadStore) bestIndexes(dirs []quad.Direction) []QuadIndex {\n\tqs.indexes.RLock()\n\tall := qs.indexes.all\n\tqs.indexes.RUnlock()\n\tvar (\n\t\tmax  int // more specific index is better\n\t\tbest QuadIndex\n\t)\n\tfor _, ind := range all {\n\t\tif len(ind.Dirs) < len(dirs) {\n\t\t\tcontinue // TODO(dennwc): allow intersecting indexes\n\t\t}\n\t\tmatch := 0\n\t\tfor i, d := range ind.Dirs {\n\t\t\tif i >= len(dirs) || !hasDir(dirs, d) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmatch++\n\t\t}\n\t\tif match == len(dirs) {\n\t\t\t// exact index match\n\t\t\treturn []QuadIndex{ind}\n\t\t}\n\t\tif match > 0 && match > max {\n\t\t\tbest = ind\n\t\t\tmax = match\n\t\t}\n\t}\n\tif max == 0 {\n\t\treturn nil\n\t}\n\t// TODO(dennwc): intersect with some other index\n\treturn []QuadIndex{best}\n}\n\nfunc (qs *QuadStore) hasPrimitive(ctx context.Context, tx kv.Tx, p *cproto.Primitive, get bool) (*cproto.Primitive, error) {\n\tif !qs.testBloom(p) {\n\t\tmQuadsBloomHit.Inc()\n\t\treturn nil, nil\n\t}\n\tmQuadsBloomMiss.Inc()\n\tinds, err := qs.bestUnique()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tunique := len(inds) != 0 && inds[0].Unique\n\tkeys := make([]kv.Key, len(inds))\n\tfor i, in := range inds {\n\t\tkeys[i] = in.KeyFor(p)\n\t}\n\tlists, err := qs.getBucketIndexes(ctx, tx, keys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar options []uint64\n\tfor len(lists) > 0 {\n\t\tif len(lists) == 1 {\n\t\t\toptions = lists[0]\n\t\t\tbreak\n\t\t}\n\t\ta, b := lists[0], lists[1]\n\t\tlists = lists[1:]\n\t\ta = intersectSortedUint64(a, b)\n\t\tlists[0] = a\n\t}\n\tif !get && unique {\n\t\treturn p, nil\n\t}\n\tfor i := len(options) - 1; i >= 0; i-- {\n\t\t// TODO: batch\n\t\tprim, err := qs.getPrimitiveFromLog(ctx, tx, options[i])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif prim.Deleted {\n\t\t\tcontinue\n\t\t}\n\t\tif prim.IsSameLink(p) {\n\t\t\treturn prim, nil\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc intersectSortedUint64(a, b []uint64) []uint64 {\n\tvar c []uint64\n\tboff := 0\nouter:\n\tfor _, x := range a {\n\t\tfor {\n\t\t\tif boff >= len(b) {\n\t\t\t\tbreak outer\n\t\t\t}\n\t\t\tif x > b[boff] {\n\t\t\t\tboff++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif x < b[boff] {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif x == b[boff] {\n\t\t\t\tc = append(c, x)\n\t\t\t\tboff++\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn c\n}\n\nfunc (qs *QuadStore) addToMapBucket(tx kv.Tx, key kv.Key, value uint64) error {\n\tif len(key) != 2 {\n\t\treturn fmt.Errorf(\"trying to add to map bucket with invalid key: %v\", key)\n\t}\n\tb, k := key[0], key[1]\n\tif len(k) == 0 {\n\t\treturn fmt.Errorf(\"trying to add to map bucket %s with key 0\", b)\n\t}\n\tif qs.mapBucket == nil {\n\t\tqs.mapBucket = make(map[string]map[string][]uint64)\n\t}\n\tbucket := string(b)\n\tm, ok := qs.mapBucket[bucket]\n\tif !ok {\n\t\tm = make(map[string][]uint64)\n\t\tqs.mapBucket[bucket] = m\n\t}\n\tm[string(k)] = append(m[string(k)], value)\n\tmIndexWriteBufferEntries.WithLabelValues(bucket).Inc()\n\treturn nil\n}\n\nfunc (qs *QuadStore) flushMapBucket(ctx context.Context, tx kv.Tx) error {\n\tbs := make([]string, 0, len(qs.mapBucket))\n\tfor k := range qs.mapBucket {\n\t\tbs = append(bs, k)\n\t}\n\tsort.Strings(bs)\n\tfor _, bucket := range bs {\n\t\tm := qs.mapBucket[bucket]\n\t\tif len(m) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tbloom := qs.mapBloom[bucket]\n\t\tmIndexWriteBufferFlushBatch.WithLabelValues(bucket).Observe(float64(len(m)))\n\t\tentryBytes := mIndexEntrySizeBytes.WithLabelValues(bucket)\n\t\tb := kv.Key{[]byte(bucket)}\n\t\tvar (\n\t\t\tkeys    []kv.Key\n\t\t\tkeysPut []kv.Key\n\t\t)\n\t\tif qs.mapBloom == nil {\n\t\t\tkeys = make([]kv.Key, 0, len(m))\n\t\t}\n\t\tfor k := range m {\n\t\t\tbk := []byte(k)\n\t\t\tif qs.mapBloom != nil && (bloom == nil || !bloom.Test(bk)) {\n\t\t\t\tkeysPut = append(keysPut, b.AppendBytes(bk))\n\t\t\t} else {\n\t\t\t\tkeys = append(keys, b.AppendBytes(bk))\n\t\t\t}\n\t\t}\n\t\tsort.Sort(kv.ByKey(keysPut))\n\t\tsort.Sort(kv.ByKey(keys))\n\t\tvals, err := tx.GetBatch(ctx, keys)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif qs.mapBloom != nil && bloom == nil {\n\t\t\tbloom = boom.NewBloomFilter(100*1000*1000, 0.05)\n\t\t\tqs.mapBloom[bucket] = bloom\n\t\t}\n\t\tfor _, k := range keysPut {\n\t\t\tl := m[string(k[1])]\n\t\t\terr = tx.Put(ctx, k, appendIndex(nil, l))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif bloom != nil {\n\t\t\t\tbloom.Add(k[1])\n\t\t\t}\n\t\t}\n\t\tfor i, k := range keys {\n\t\t\tl := m[string(k[1])]\n\t\t\tbuf := appendIndex(vals[i], l)\n\t\t\tentryBytes.Observe(float64(len(buf)))\n\t\t\terr = tx.Put(ctx, k, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif bloom != nil {\n\t\t\t\tbloom.Add(k[1])\n\t\t\t}\n\t\t}\n\t\tmIndexWriteBufferEntries.WithLabelValues(bucket).Set(0)\n\t}\n\tqs.mapBucket = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) indexSchema(tx kv.Tx, p *cproto.Primitive) error {\n\treturn nil\n}\n\nfunc (qs *QuadStore) addToLog(ctx context.Context, tx kv.Tx, p *cproto.Primitive) error {\n\tbuf, err := proto.Marshal(p)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := tx.Put(ctx, logIndex.Append(uint64KeyBytes(p.ID)), buf); err != nil {\n\t\treturn err\n\t}\n\tmPrimitiveAppend.Inc()\n\treturn nil\n}\n\nfunc createNodePrimitive(v quad.Value) (*cproto.Primitive, error) {\n\tp := &cproto.Primitive{}\n\tb, err := pquads.MarshalValue(v)\n\tif err != nil {\n\t\treturn p, err\n\t}\n\tp.Value = b\n\tp.Timestamp = time.Now().UnixNano()\n\treturn p, nil\n}\n\nfunc (qs *QuadStore) resolveQuadValue(ctx context.Context, tx kv.Tx, v quad.Value) (uint64, error) {\n\tout, err := qs.resolveQuadValues(ctx, tx, []quad.Value{v})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn out[0], nil\n}\n\nfunc bucketKeyForVal(v quad.Value) kv.Key {\n\thash := refs.HashOf(v)\n\treturn bucketKeyForHash(hash)\n}\n\nfunc bucketKeyForHash(h refs.ValueHash) kv.Key {\n\treturn bucketForVal(h[0], h[1]).AppendBytes(h[:])\n}\n\nfunc bucketKeyForHashRefs(h refs.ValueHash) kv.Key {\n\treturn bucketForValRefs(h[0], h[1]).AppendBytes(h[:])\n}\n\nfunc (qs *QuadStore) resolveQuadValues(ctx context.Context, tx kv.Tx, vals []quad.Value) ([]uint64, error) {\n\tout := make([]uint64, len(vals))\n\tinds := make([]int, 0, len(vals))\n\tkeys := make([]kv.Key, 0, len(vals))\n\tfor i, v := range vals {\n\t\tif iri, ok := v.(quad.IRI); ok {\n\t\t\tif x, ok := qs.valueLRU.Get(string(iri)); ok {\n\t\t\t\tout[i] = x.(uint64)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else if v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tinds = append(inds, i)\n\t\tkeys = append(keys, bucketKeyForVal(v))\n\t}\n\tif len(keys) == 0 {\n\t\treturn out, nil\n\t}\n\tresp, err := tx.GetBatch(ctx, keys)\n\tif err != nil {\n\t\treturn out, err\n\t}\n\tfor i, b := range resp {\n\t\tif len(b) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tind := inds[i]\n\t\tout[ind], _ = binary.Uvarint(b)\n\t\tif iri, ok := vals[ind].(quad.IRI); ok && out[ind] != 0 {\n\t\t\tqs.valueLRU.Put(string(iri), uint64(out[ind]))\n\t\t}\n\t}\n\treturn out, nil\n}\n\nfunc uint64toBytes(x uint64) []byte {\n\tb := make([]byte, binary.MaxVarintLen64)\n\treturn uint64toBytesAt(x, b)\n}\n\nfunc uint64toBytesAt(x uint64, bytes []byte) []byte {\n\tn := binary.PutUvarint(bytes, x)\n\treturn bytes[:n]\n}\n\nfunc uint64KeyBytes(x uint64) kv.Key {\n\tk := make([]byte, 8)\n\tquadKeyEnc.PutUint64(k, x)\n\treturn kv.Key{k}\n}\n\nfunc (qs *QuadStore) getPrimitivesFromLog(ctx context.Context, tx kv.Tx, keys []uint64) ([]*cproto.Primitive, error) {\n\tbkeys := make([]kv.Key, len(keys))\n\tfor i, k := range keys {\n\t\tbkeys[i] = logIndex.Append(uint64KeyBytes(k))\n\t}\n\tvals, err := tx.GetBatch(ctx, bkeys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmPrimitiveFetch.Add(float64(len(vals)))\n\tout := make([]*cproto.Primitive, len(keys))\n\tvar last error\n\tfor i, v := range vals {\n\t\tif v == nil {\n\t\t\tmPrimitiveFetchMiss.Inc()\n\t\t\tcontinue\n\t\t}\n\t\tvar p cproto.Primitive\n\t\tif err = proto.Unmarshal(v, &p); err != nil {\n\t\t\tlast = err\n\t\t} else {\n\t\t\tout[i] = &p\n\t\t}\n\t}\n\treturn out, last\n}\n\nfunc (qs *QuadStore) getPrimitiveFromLog(ctx context.Context, tx kv.Tx, k uint64) (*cproto.Primitive, error) {\n\tout, err := qs.getPrimitivesFromLog(ctx, tx, []uint64{k})\n\tif err != nil {\n\t\treturn nil, err\n\t} else if out[0] == nil {\n\t\treturn nil, kv.ErrNotFound\n\t}\n\treturn out[0], nil\n}\n\nfunc (qs *QuadStore) initBloomFilter(ctx context.Context) error {\n\tif qs.exists.disabled {\n\t\treturn nil\n\t}\n\tqs.exists.buf = make([]byte, 3*8)\n\tqs.exists.DeletableBloomFilter = boom.NewDeletableBloomFilter(100*1000*1000, 120, 0.05)\n\treturn kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tp := cproto.Primitive{}\n\t\tit := tx.Scan(ctx, options.WithPrefixKV(logIndex))\n\t\tdefer it.Close()\n\t\tfor it.Next(ctx) {\n\t\t\tv := it.Val()\n\t\t\tp = cproto.Primitive{}\n\t\t\terr := proto.Unmarshal(v, &p)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif p.IsNode() {\n\t\t\t\tcontinue\n\t\t\t} else if p.Deleted {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\twritePrimToBuf(&p, qs.exists.buf)\n\t\t\tqs.exists.Add(qs.exists.buf)\n\t\t}\n\t\treturn it.Err()\n\t})\n}\n\nfunc (qs *QuadStore) testBloom(p *cproto.Primitive) bool {\n\tif qs.exists.disabled {\n\t\treturn true // false positives are expected\n\t}\n\tqs.exists.Lock()\n\tdefer qs.exists.Unlock()\n\twritePrimToBuf(p, qs.exists.buf)\n\treturn qs.exists.Test(qs.exists.buf)\n}\n\nfunc (qs *QuadStore) bloomRemove(p *cproto.Primitive) {\n\tif qs.exists.disabled {\n\t\treturn\n\t}\n\tqs.exists.Lock()\n\tdefer qs.exists.Unlock()\n\twritePrimToBuf(p, qs.exists.buf)\n\tqs.exists.TestAndRemove(qs.exists.buf)\n}\n\nfunc (qs *QuadStore) bloomAdd(p *cproto.Primitive) {\n\tif qs.exists.disabled {\n\t\treturn\n\t}\n\tqs.exists.Lock()\n\tdefer qs.exists.Unlock()\n\twritePrimToBuf(p, qs.exists.buf)\n\tqs.exists.Add(qs.exists.buf)\n}\n\nfunc writePrimToBuf(p *cproto.Primitive, buf []byte) {\n\tquadKeyEnc.PutUint64(buf[0:8], p.Subject)\n\tquadKeyEnc.PutUint64(buf[8:16], p.Predicate)\n\tquadKeyEnc.PutUint64(buf[16:24], p.Object)\n}\n\ntype Int64Set []uint64\n\nfunc (a Int64Set) Len() int           { return len(a) }\nfunc (a Int64Set) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\nfunc (a Int64Set) Less(i, j int) bool { return a[i] < a[j] }\n"
  },
  {
    "path": "graph/kv/indexing_test.go",
    "content": "package kv\n\nimport \"testing\"\n\nfunc TestIntersectSorted(t *testing.T) {\n\ttt := []struct {\n\t\ta      []uint64\n\t\tb      []uint64\n\t\texpect []uint64\n\t}{\n\t\t{\n\t\t\ta:      []uint64{1, 2, 3, 4, 5, 6},\n\t\t\tb:      []uint64{2, 4, 6, 8, 10},\n\t\t\texpect: []uint64{2, 4, 6},\n\t\t},\n\t\t{\n\t\t\ta:      []uint64{6, 7, 8, 9, 10, 11},\n\t\t\tb:      []uint64{1, 2, 3, 4, 5, 6},\n\t\t\texpect: []uint64{6},\n\t\t},\n\t}\n\n\tfor i, x := range tt {\n\t\tc := intersectSortedUint64(x.a, x.b)\n\t\tif len(c) != len(x.expect) {\n\t\t\tt.Errorf(\"unexpected length: %d expected %d for test %d\", len(c), len(x.expect), i)\n\t\t}\n\t\tfor i, y := range c {\n\t\t\tif y != x.expect[i] {\n\t\t\t\tt.Errorf(\"unexpected entry: %#v expected %#v for test %d\", c, x.expect, i)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestIndexlist(t *testing.T) {\n\tinit := []uint64{5, 10, 2340, 32432, 3243366}\n\tb := appendIndex(nil, init)\n\tout, err := decodeIndex(b)\n\tif err != nil {\n\t\tt.Fatalf(\"couldn't decodeIndex: %s\", err)\n\t}\n\tif len(out) != len(init) {\n\t\tt.Fatalf(\"mismatched lengths. got %#v expected %#v\", out, init)\n\t}\n\tfor i := 0; i < len(out); i++ {\n\t\tif out[i] != init[i] {\n\t\t\tt.Fatalf(\"mismatched element %d. got %#v expected %#v\", i, out, init)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "graph/kv/iterators.go",
    "content": "package kv\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/hidal-go/hidalgo/kv\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n)\n\nfunc (qs *QuadStore) NodesAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(true, nil)\n}\n\nfunc (qs *QuadStore) QuadsAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(false, nil)\n}\n\nfunc (qs *QuadStore) indexSize(ctx context.Context, ind QuadIndex, vals []uint64) (refs.Size, error) {\n\tvar sz int64\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tval, err := tx.Get(ctx, ind.Key(vals))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsz, err = countIndex(val)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn refs.Size{}, err\n\t}\n\tif len(ind.Dirs) == len(vals) {\n\t\treturn refs.Size{\n\t\t\tValue: sz,\n\t\t\tExact: true,\n\t\t}, nil\n\t}\n\treturn refs.Size{\n\t\tValue: 1 + sz/2,\n\t\tExact: false,\n\t}, nil\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, v graph.Ref) (refs.Size, error) {\n\tvi, ok := v.(Int64Value)\n\tif !ok {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t}\n\tqs.indexes.RLock()\n\tall := qs.indexes.all\n\tqs.indexes.RUnlock()\n\tfor _, ind := range all {\n\t\tif len(ind.Dirs) == 1 && ind.Dirs[0] == d {\n\t\t\treturn qs.indexSize(ctx, ind, []uint64{uint64(vi)})\n\t\t}\n\t}\n\tst, err := qs.Stats(ctx, false)\n\tif err != nil {\n\t\treturn refs.Size{}, err\n\t}\n\treturn refs.Size{\n\t\tValue: 1 + st.Quads.Value/2,\n\t\tExact: false,\n\t}, nil\n}\n\nfunc (qs *QuadStore) QuadIterator(dir quad.Direction, v graph.Ref) iterator.Shape {\n\tif v == nil {\n\t\treturn iterator.NewNull()\n\t}\n\tvi, ok := v.(Int64Value)\n\tif !ok {\n\t\treturn iterator.NewError(fmt.Errorf(\"unexpected node type: %T\", v))\n\t}\n\t// Find the best index for this direction.\n\tif ind := qs.bestIndexes([]quad.Direction{dir}); len(ind) == 1 {\n\t\t// this will scan the prefix automatically\n\t\treturn qs.newQuadIterator(ind[0], []uint64{uint64(vi)})\n\t}\n\t// Fallback: iterate all quads and check the corresponding direction.\n\treturn qs.newAllIterator(false, &constraint{\n\t\tdir: dir,\n\t\tval: vi,\n\t})\n}\n\nfunc (qs *QuadStore) OptimizeShape(ctx context.Context, s shape.Shape) (shape.Shape, bool) {\n\tswitch s := s.(type) {\n\tcase shape.QuadsAction:\n\t\treturn qs.optimizeQuadsAction(s)\n\t}\n\treturn s, false\n}\n\nfunc (qs *QuadStore) optimizeQuadsAction(s shape.QuadsAction) (shape.Shape, bool) {\n\tif len(s.Filter) == 0 {\n\t\treturn s, false\n\t}\n\tdirs := make([]quad.Direction, 0, len(s.Filter))\n\tfor d := range s.Filter {\n\t\tdirs = append(dirs, d)\n\t}\n\tind := qs.bestIndexes(dirs)\n\tif len(ind) != 1 {\n\t\treturn s, false // TODO(dennwc): allow intersecting indexes\n\t}\n\tquads := IndexScan{Index: ind[0]}\n\tfor _, d := range ind[0].Dirs {\n\t\tv, ok := s.Filter[d].(Int64Value)\n\t\tif !ok {\n\t\t\treturn s, false\n\t\t}\n\t\tquads.Values = append(quads.Values, uint64(v))\n\t}\n\treturn s.SimplifyFrom(quads), true\n}\n\ntype IndexScan struct {\n\tIndex  QuadIndex\n\tValues []uint64\n}\n\nfunc (s IndexScan) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tkqs, ok := qs.(*QuadStore)\n\tif !ok {\n\t\treturn iterator.NewError(fmt.Errorf(\"expected KV quadstore, got: %T\", qs))\n\t}\n\treturn kqs.newQuadIterator(s.Index, s.Values)\n}\n\nfunc (s IndexScan) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\treturn s, false\n}\n"
  },
  {
    "path": "graph/kv/kvtest/kvtest.go",
    "content": "package kvtest\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/graph/kv\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n)\n\ntype DatabaseFunc func(t testing.TB) (hkv.KV, graph.Options, func())\n\ntype Config struct {\n\tAlwaysRunIntegration bool\n}\n\nfunc (c Config) quadStore() *graphtest.Config {\n\treturn &graphtest.Config{\n\t\tNoPrimitives:         true,\n\t\tAlwaysRunIntegration: c.AlwaysRunIntegration,\n\t}\n}\n\nfunc newQuadStoreFunc(gen DatabaseFunc, bloom bool) testutil.DatabaseFunc {\n\treturn func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\treturn newQuadStore(t, gen, bloom)\n\t}\n}\n\nfunc NewQuadStoreFunc(gen DatabaseFunc) testutil.DatabaseFunc {\n\treturn newQuadStoreFunc(gen, true)\n}\n\nfunc newQuadStore(t testing.TB, gen DatabaseFunc, bloom bool) (graph.QuadStore, graph.Options) {\n\tdb, opt, closer := gen(t)\n\tif opt == nil {\n\t\topt = make(graph.Options)\n\t}\n\tif !bloom {\n\t\topt[kv.OptNoBloom] = true\n\t}\n\terr := kv.Init(db, opt)\n\tif err != nil {\n\t\tdb.Close()\n\t\tcloser()\n\t\trequire.Fail(t, \"init failed\", \"%v\", err)\n\t}\n\tkdb, err := kv.New(db, opt)\n\tif err != nil {\n\t\tdb.Close()\n\t\tcloser()\n\t\trequire.Fail(t, \"create failed\", \"%v\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tkdb.Close()\n\t})\n\treturn kdb, opt\n}\n\nfunc NewQuadStore(t testing.TB, gen DatabaseFunc) (graph.QuadStore, graph.Options) {\n\treturn newQuadStore(t, gen, true)\n}\n\nfunc TestAll(t *testing.T, gen DatabaseFunc, conf *Config) {\n\tif conf == nil {\n\t\tconf = &Config{}\n\t}\n\tqsgen := NewQuadStoreFunc(gen)\n\tt.Run(\"qs\", func(t *testing.T) {\n\t\tgraphtest.TestAll(t, qsgen, conf.quadStore())\n\t})\n\tqsgenNoBloom := newQuadStoreFunc(gen, false)\n\tt.Run(\"qs-no-bloom\", func(t *testing.T) {\n\t\tgraphtest.TestAll(t, qsgenNoBloom, conf.quadStore())\n\t})\n\tt.Run(\"optimize\", func(t *testing.T) {\n\t\ttestOptimize(t, gen, conf)\n\t})\n}\n\nfunc testOptimize(t *testing.T, gen DatabaseFunc, _ *Config) {\n\tctx := context.TODO()\n\tqs, opts := NewQuadStore(t, gen)\n\n\ttestutil.MakeWriter(t, qs, opts, graphtest.MakeQuadSet()...)\n\n\t// With an linksto-fixed pair\n\tlto := shape.BuildIterator(ctx, qs, shape.Quads{\n\t\t{Dir: quad.Object, Values: shape.Lookup{quad.Raw(\"F\")}},\n\t})\n\n\toldIt := shape.BuildIterator(ctx, qs, shape.Quads{\n\t\t{Dir: quad.Object, Values: shape.Lookup{quad.Raw(\"F\")}},\n\t}).Iterate()\n\tdefer oldIt.Close()\n\tnewIts, ok := lto.Optimize(ctx)\n\tif ok {\n\t\tt.Errorf(\"unexpected optimization step\")\n\t}\n\tif _, ok := newIts.(*kv.QuadIterator); !ok {\n\t\tt.Errorf(\"Optimized iterator type does not match original, got: %T\", newIts)\n\t}\n\tnewIt := newIts.Iterate()\n\tdefer newIt.Close()\n\n\tnewQuads := graphtest.IteratedQuadsNext(t, qs, newIt)\n\toldQuads := graphtest.IteratedQuadsNext(t, qs, oldIt)\n\tif !reflect.DeepEqual(newQuads, oldQuads) {\n\t\tt.Errorf(\"Optimized iteration does not match original\")\n\t}\n\n\toldIt.Next(ctx)\n\toldResults := make(map[string]graph.Ref)\n\toldIt.TagResults(oldResults)\n\tnewIt.Next(ctx)\n\tnewResults := make(map[string]graph.Ref)\n\tnewIt.TagResults(newResults)\n\tif !reflect.DeepEqual(newResults, oldResults) {\n\t\tt.Errorf(\"Discordant tag results, new:%v old:%v\", newResults, oldResults)\n\t}\n}\n\nfunc BenchmarkAll(t *testing.B, gen DatabaseFunc, conf *Config) {\n\tif conf == nil {\n\t\tconf = &Config{}\n\t}\n\tqsgen := NewQuadStoreFunc(gen)\n\tt.Run(\"qs\", func(t *testing.B) {\n\t\tgraphtest.BenchmarkAll(t, qsgen, conf.quadStore())\n\t})\n}\n"
  },
  {
    "path": "graph/kv/leveldb/leveldb.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage leveldb\n\nimport (\n\t\"os\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/flat\"\n\t\"github.com/hidal-go/hidalgo/kv/flat/leveldb\"\n\t\"github.com/syndtr/goleveldb/leveldb/opt\"\n)\n\nconst (\n\tType = leveldb.Name\n)\n\nfunc Create(path string, m graph.Options) (hkv.KV, error) {\n\terr := os.MkdirAll(path, 0700)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdb, err := leveldb.Open(path, &opt.Options{\n\t\tErrorIfExist: true,\n\t})\n\tif os.IsExist(err) {\n\t\treturn nil, graph.ErrDatabaseExists\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\tnosync, _ := m.BoolKey(\"nosync\", false)\n\tdb.SetWriteOptions(&opt.WriteOptions{\n\t\tSync: !nosync,\n\t})\n\treturn flat.Upgrade(db), nil\n}\n\nfunc Open(path string, m graph.Options) (hkv.KV, error) {\n\tdb, err := leveldb.Open(path, &opt.Options{\n\t\tErrorIfMissing: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnosync, _ := m.BoolKey(\"nosync\", false)\n\tdb.SetWriteOptions(&opt.WriteOptions{\n\t\tSync: !nosync,\n\t})\n\treturn flat.Upgrade(db), nil\n}\n"
  },
  {
    "path": "graph/kv/leveldb/leveldb_test.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage leveldb\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv/kvtest\"\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n)\n\nfunc makeLeveldb(t testing.TB) (hkv.KV, graph.Options, func()) {\n\ttmpDir, err := ioutil.TempDir(os.TempDir(), \"cayley_test_\"+Type)\n\tif err != nil {\n\t\tt.Fatalf(\"Could not create working directory: %v\", err)\n\t}\n\tdb, err := Create(tmpDir, nil)\n\tif err != nil {\n\t\tos.RemoveAll(tmpDir)\n\t\tt.Fatal(\"Failed to create Bolt database.\", err)\n\t}\n\treturn db, nil, func() {\n\t\tdb.Close()\n\t\tos.RemoveAll(tmpDir)\n\t}\n}\n\nfunc TestLeveldb(t *testing.T) {\n\tkvtest.TestAll(t, makeLeveldb, nil)\n}\n\nfunc BenchmarkLeveldb(b *testing.B) {\n\tkvtest.BenchmarkAll(b, makeLeveldb, nil)\n}\n"
  },
  {
    "path": "graph/kv/metrics.go",
    "content": "package kv\n\nimport (\n\t\"context\"\n\n\t\"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n)\n\nvar (\n\tmApplyBatch = promauto.NewHistogram(prometheus.HistogramOpts{\n\t\tName: \"cayley_apply_deltas_batch\",\n\t\tHelp: \"Number of quads in a buffer for ApplyDeltas or WriteQuads.\",\n\t})\n\tmApplySeconds = promauto.NewHistogram(prometheus.HistogramOpts{\n\t\tName: \"cayley_apply_deltas_seconds\",\n\t\tHelp: \"Time to write a buffer in ApplyDeltas or WriteQuads.\",\n\t})\n\n\tmNodesNew = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_nodes_new_count\",\n\t\tHelp: \"Number new nodes created.\",\n\t})\n\tmNodesUpd = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_nodes_upd_count\",\n\t\tHelp: \"Number of node refcount updates.\",\n\t})\n\tmNodesDel = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_nodes_del_count\",\n\t\tHelp: \"Number of node deleted.\",\n\t})\n\n\tmQuadsBloomHit = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_quads_bloom_hits\",\n\t\tHelp: \"Number of times the quad bloom filter returned a negative result.\",\n\t})\n\tmQuadsBloomMiss = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_quads_bloom_miss\",\n\t\tHelp: \"Number of times the quad bloom filter returned a positive result.\",\n\t})\n\n\tmPrimitiveFetch = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_primitive_fetch\",\n\t\tHelp: \"Number of primitives fetched from KV.\",\n\t})\n\tmPrimitiveFetchMiss = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_primitive_fetch_miss\",\n\t\tHelp: \"Number of primitives that were not found in KV.\",\n\t})\n\tmPrimitiveAppend = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_primitive_append\",\n\t\tHelp: \"Number of primitives appended to log.\",\n\t})\n\n\tmIndexWriteBufferEntries = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"cayley_kv_index_buffer_entries\",\n\t\tHelp: \"Number of entries in the index write buffer.\",\n\t}, []string{\"index\"})\n\tmIndexWriteBufferFlushBatch = promauto.NewHistogramVec(prometheus.HistogramOpts{\n\t\tName: \"cayley_kv_index_buffer_flush_batch\",\n\t\tHelp: \"Number of entries in the batch for flushing index entries.\",\n\t}, []string{\"index\"})\n\tmIndexEntrySizeBytes = promauto.NewHistogramVec(prometheus.HistogramOpts{\n\t\tName: \"cayley_kv_index_entry_size_bytes\",\n\t\tHelp: \"Size of a single index entry.\",\n\t}, []string{\"index\"})\n\n\tmKVGet = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_get_count\",\n\t\tHelp: \"Number of get KV calls.\",\n\t})\n\tmKVGetMiss = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_get_miss\",\n\t\tHelp: \"Number of get KV calls that found no value.\",\n\t})\n\tmKVGetSize = promauto.NewHistogram(prometheus.HistogramOpts{\n\t\tName: \"cayley_kv_get_size\",\n\t\tHelp: \"Size of values returned from KV.\",\n\t})\n\tmKVPut = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_put_count\",\n\t\tHelp: \"Number of put KV calls.\",\n\t})\n\tmKVPutSize = promauto.NewHistogram(prometheus.HistogramOpts{\n\t\tName: \"cayley_kv_put_size\",\n\t\tHelp: \"Size of values put to KV.\",\n\t})\n\tmKVDel = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_del_count\",\n\t\tHelp: \"Number of del KV calls.\",\n\t})\n\tmKVScan = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_scan_count\",\n\t\tHelp: \"Number of scan KV calls.\",\n\t})\n\tmKVCommit = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_commit\",\n\t\tHelp: \"Number of KV commits.\",\n\t})\n\tmKVCommitSeconds = promauto.NewHistogram(prometheus.HistogramOpts{\n\t\tName: \"cayley_kv_commit_seconds\",\n\t\tHelp: \"Time to commit to KV.\",\n\t})\n\tmKVRollback = promauto.NewCounter(prometheus.CounterOpts{\n\t\tName: \"cayley_kv_rollback\",\n\t\tHelp: \"Number of KV rollbacks.\",\n\t})\n)\n\nfunc wrapTx(tx kv.Tx) kv.Tx {\n\treturn &mTx{tx: tx}\n}\n\ntype mTx struct {\n\ttx   kv.Tx\n\tdone bool\n}\n\nfunc (tx *mTx) Commit(ctx context.Context) error {\n\tif !tx.done {\n\t\ttx.done = true\n\t\tmKVCommit.Inc()\n\t\tdefer prometheus.NewTimer(mKVCommitSeconds).ObserveDuration()\n\t}\n\treturn tx.tx.Commit(ctx)\n}\n\nfunc (tx *mTx) Close() error {\n\tif !tx.done {\n\t\ttx.done = true\n\t\tmKVRollback.Inc()\n\t}\n\treturn tx.tx.Close()\n}\n\nfunc (tx *mTx) Get(ctx context.Context, key kv.Key) (kv.Value, error) {\n\tmKVGet.Inc()\n\tval, err := tx.tx.Get(ctx, key)\n\tif err == kv.ErrNotFound {\n\t\tmKVGetMiss.Inc()\n\t} else if err == nil {\n\t\tmKVGetSize.Observe(float64(len(val)))\n\t}\n\treturn val, err\n}\n\nfunc (tx *mTx) GetBatch(ctx context.Context, keys []kv.Key) ([]kv.Value, error) {\n\tmKVGet.Add(float64(len(keys)))\n\tvals, err := tx.tx.GetBatch(ctx, keys)\n\tfor _, v := range vals {\n\t\tif v == nil {\n\t\t\tmKVGetMiss.Inc()\n\t\t} else {\n\t\t\tmKVGetSize.Observe(float64(len(v)))\n\t\t}\n\t}\n\treturn vals, err\n}\n\nfunc (tx *mTx) Put(ctx context.Context, k kv.Key, v kv.Value) error {\n\tmKVPut.Inc()\n\tmKVPutSize.Observe(float64(len(v)))\n\treturn tx.tx.Put(ctx, k, v)\n}\n\nfunc (tx *mTx) Del(ctx context.Context, k kv.Key) error {\n\tmKVDel.Inc()\n\treturn tx.tx.Del(ctx, k)\n}\n\nfunc (tx *mTx) Scan(ctx context.Context, opts ...kv.IteratorOption) kv.Iterator {\n\tmKVScan.Inc()\n\treturn tx.tx.Scan(ctx, opts...)\n}\n"
  },
  {
    "path": "graph/kv/quad_iterator.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kv\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/hidal-go/hidalgo/kv/options\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/proto\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\ntype QuadIterator struct {\n\tqs   *QuadStore\n\tind  QuadIndex\n\tvals []uint64\n\n\tsize refs.Size\n\terr  error\n}\n\nfunc (qs *QuadStore) newQuadIterator(ind QuadIndex, vals []uint64) *QuadIterator {\n\treturn &QuadIterator{\n\t\tqs:   qs,\n\t\tind:  ind,\n\t\tvals: vals,\n\t\tsize: refs.Size{Value: -1},\n\t}\n}\n\nfunc (it *QuadIterator) Iterate() iterator.Scanner {\n\treturn it.qs.newQuadIteratorNext(it.ind, it.vals)\n}\n\nfunc (it *QuadIterator) Lookup() iterator.Index {\n\treturn it.qs.newQuadIteratorContains(it.ind, it.vals)\n}\n\nfunc (it *QuadIterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *QuadIterator) getSize(ctx context.Context) (refs.Size, error) {\n\tif it.err != nil {\n\t\treturn refs.Size{}, it.err\n\t} else if it.size.Value >= 0 {\n\t\treturn it.size, nil\n\t}\n\tif len(it.ind.Dirs) == len(it.vals) {\n\t\tsz, err := it.qs.indexSize(ctx, it.ind, it.vals)\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t\treturn refs.Size{}, it.err\n\t\t}\n\t\tit.size = sz\n\t\treturn sz, nil\n\t}\n\tsz := refs.Size{Value: 1 + it.qs.Size()/2, Exact: false}\n\tit.size = sz\n\treturn sz, nil\n}\n\nfunc (it *QuadIterator) String() string {\n\treturn fmt.Sprintf(\"KVQuads(%v)\", it.ind)\n}\n\nfunc (it *QuadIterator) Sorted() bool { return true }\n\nfunc (it *QuadIterator) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\treturn it, false\n}\n\nfunc (it *QuadIterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\ts, err := it.getSize(ctx)\n\treturn iterator.Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     2,\n\t\tSize:         s,\n\t}, err\n}\n\ntype quadIteratorNext struct {\n\tqs   *QuadStore\n\tind  QuadIndex\n\tvals []uint64\n\n\ttx   kv.Tx\n\tit   kv.Iterator\n\tdone bool\n\n\terr  error\n\toff  int\n\tids  []uint64\n\tbuf  []*proto.Primitive\n\tprim *proto.Primitive\n}\n\nfunc (qs *QuadStore) newQuadIteratorNext(ind QuadIndex, vals []uint64) *quadIteratorNext {\n\treturn &quadIteratorNext{\n\t\tqs:   qs,\n\t\tind:  ind,\n\t\tvals: vals,\n\t}\n}\n\nfunc (it *quadIteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *quadIteratorNext) Close() error {\n\tif it.it != nil {\n\t\tif err := it.it.Close(); err != nil && it.err == nil {\n\t\t\tit.err = err\n\t\t}\n\t\tif err := it.tx.Close(); err != nil && it.err == nil {\n\t\t\tit.err = err\n\t\t}\n\t\tit.it = nil\n\t\tit.tx = nil\n\t}\n\treturn it.err\n}\n\nfunc (it *quadIteratorNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *quadIteratorNext) Result() graph.Ref {\n\tif it.off < 0 || it.prim == nil {\n\t\treturn nil\n\t}\n\treturn it.prim\n}\n\nfunc (it *quadIteratorNext) ensureTx(ctx context.Context) bool {\n\tif it.tx != nil {\n\t\treturn true\n\t}\n\tit.tx, it.err = it.qs.db.Tx(ctx, false)\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tit.tx = wrapTx(it.tx)\n\treturn true\n}\n\nfunc (it *quadIteratorNext) Next(ctx context.Context) bool {\n\tit.prim = nil\n\tif it.err != nil || it.done {\n\t\treturn false\n\t}\n\tif it.it == nil {\n\t\tif !it.ensureTx(ctx) {\n\t\t\treturn false\n\t\t}\n\t\tit.it = it.tx.Scan(ctx, options.WithPrefixKV(it.ind.Key(it.vals)))\n\t\tif err := it.Err(); err != nil {\n\t\t\tit.err = err\n\t\t\treturn false\n\t\t}\n\t}\n\tfor {\n\t\tif len(it.buf) == 0 {\n\t\t\tfor len(it.ids[it.off:]) == 0 {\n\t\t\t\tit.off = 0\n\t\t\t\tit.ids = nil\n\t\t\t\tit.buf = nil\n\t\t\t\tif !it.it.Next(ctx) {\n\t\t\t\t\tit.Close()\n\t\t\t\t\tit.done = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tit.ids, it.err = decodeIndex(it.it.Val())\n\t\t\t\tif it.err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\tids := it.ids[it.off:]\n\t\t\tif len(ids) > nextBatch {\n\t\t\t\tids = ids[:nextBatch]\n\t\t\t}\n\t\t\tit.buf, it.err = it.qs.getPrimitivesFromLog(ctx, it.tx, ids)\n\t\t\tif it.err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t} else {\n\t\t\tit.buf, it.off = it.buf[1:], it.off+1\n\t\t}\n\t\tfor ; len(it.buf) > 0; it.buf, it.off = it.buf[1:], it.off+1 {\n\t\t\tp := it.buf[0]\n\t\t\tif p == nil || p.Deleted {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// TODO(dennwc): shouldn't this check the horizon?\n\t\t\tit.prim = p\n\t\t\treturn true\n\t\t}\n\t}\n}\n\nfunc (it *quadIteratorNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *quadIteratorNext) String() string {\n\treturn fmt.Sprintf(\"KVQuadsNext(%v)\", it.ind)\n}\n\nfunc (it *quadIteratorNext) Sorted() bool { return true }\n\ntype quadIteratorContains struct {\n\tqs   *QuadStore\n\tind  QuadIndex\n\tvals []uint64\n\n\terr  error\n\tprim *proto.Primitive\n}\n\nfunc (qs *QuadStore) newQuadIteratorContains(ind QuadIndex, vals []uint64) *quadIteratorContains {\n\treturn &quadIteratorContains{\n\t\tqs:   qs,\n\t\tind:  ind,\n\t\tvals: vals,\n\t}\n}\n\nfunc (it *quadIteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *quadIteratorContains) Close() error {\n\treturn it.err\n}\n\nfunc (it *quadIteratorContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *quadIteratorContains) Result() graph.Ref {\n\tif it.prim == nil {\n\t\treturn nil\n\t}\n\treturn it.prim\n}\n\nfunc (it *quadIteratorContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *quadIteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tit.prim = nil\n\t// TODO(dennwc): shouldn't this check the horizon?\n\tp, ok := v.(*proto.Primitive)\n\tif !ok {\n\t\treturn false\n\t}\n\tfor i, v := range it.vals {\n\t\tif p.GetDirection(it.ind.Dirs[i]) != v {\n\t\t\treturn false\n\t\t}\n\t}\n\tit.prim = p\n\treturn true\n}\n\nfunc (it *quadIteratorContains) String() string {\n\treturn fmt.Sprintf(\"KVQuadsContains(%v)\", it.ind)\n}\n\nfunc (it *quadIteratorContains) Sorted() bool { return true }\n"
  },
  {
    "path": "graph/kv/quadstore.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kv\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n\t\"github.com/hidal-go/hidalgo/kv\"\n\tboom \"github.com/tylertreat/BoomFilters\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/proto\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/internal/lru\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n)\n\nvar (\n\tErrNoBucket  = errors.New(\"kv: no bucket\")\n\tErrEmptyPath = errors.New(\"kv: path to the database must be specified\")\n)\n\ntype Registration struct {\n\tNewFunc      NewFunc\n\tInitFunc     InitFunc\n\tIsPersistent bool\n}\n\ntype InitFunc func(string, graph.Options) (kv.KV, error)\ntype NewFunc func(string, graph.Options) (kv.KV, error)\n\nfunc Register(name string, r Registration) {\n\tgraph.RegisterQuadStore(name, graph.QuadStoreRegistration{\n\t\tInitFunc: func(addr string, opt graph.Options) error {\n\t\t\tif !r.IsPersistent {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tkv, err := r.InitFunc(addr, opt)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer kv.Close()\n\t\t\tif err = Init(kv, opt); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn kv.Close()\n\t\t},\n\t\tNewFunc: func(addr string, opt graph.Options) (graph.QuadStore, error) {\n\t\t\tkv, err := r.NewFunc(addr, opt)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif !r.IsPersistent {\n\t\t\t\tif err = Init(kv, opt); err != nil {\n\t\t\t\t\tkv.Close()\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn New(kv, opt)\n\t\t},\n\t\tIsPersistent: r.IsPersistent,\n\t})\n}\n\nconst (\n\tlatestDataVersion   = 2\n\tenvKVDefaultIndexes = \"CAYLEY_KV_INDEXES\"\n)\n\nvar (\n\t_ refs.BatchNamer = (*QuadStore)(nil)\n\t_ shape.Optimizer = (*QuadStore)(nil)\n)\n\ntype QuadStore struct {\n\tdb kv.KV\n\n\tindexes struct {\n\t\tsync.RWMutex\n\t\tall []QuadIndex\n\t\t// indexes used to detect duplicate quads\n\t\texists []QuadIndex\n\t}\n\n\tvalueLRU *lru.Cache\n\n\twriter    sync.Mutex\n\tmapBucket map[string]map[string][]uint64\n\tmapBloom  map[string]*boom.BloomFilter\n\tmapNodes  *boom.BloomFilter\n\n\texists struct {\n\t\tdisabled bool\n\t\tsync.Mutex\n\t\tbuf []byte\n\t\t*boom.DeletableBloomFilter\n\t}\n}\n\nfunc newQuadStore(kv kv.KV) *QuadStore {\n\treturn &QuadStore{db: kv}\n}\n\nfunc Init(kv kv.KV, opt graph.Options) error {\n\tctx := context.TODO()\n\tqs := newQuadStore(kv)\n\tif data := os.Getenv(envKVDefaultIndexes); data != \"\" {\n\t\tqs.indexes.all = nil\n\t\tif err := json.Unmarshal([]byte(data), &qs.indexes); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif qs.indexes.all == nil {\n\t\tqs.indexes.all = DefaultQuadIndexes\n\t}\n\tif _, err := qs.getMetadata(ctx); err == nil {\n\t\treturn graph.ErrDatabaseExists\n\t} else if err != ErrNoBucket {\n\t\treturn err\n\t}\n\tupfront, err := opt.BoolKey(\"upfront\", false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := qs.createBuckets(ctx, upfront); err != nil {\n\t\treturn err\n\t}\n\tif err := setVersion(ctx, qs.db, latestDataVersion); err != nil {\n\t\treturn err\n\t}\n\tif err := qs.writeIndexesMeta(ctx); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nconst (\n\tOptNoBloom = \"no_bloom\"\n)\n\nfunc New(kv kv.KV, opt graph.Options) (graph.QuadStore, error) {\n\tctx := context.TODO()\n\tqs := newQuadStore(kv)\n\tif vers, err := qs.getMetadata(ctx); err == ErrNoBucket {\n\t\treturn nil, graph.ErrNotInitialized\n\t} else if err != nil {\n\t\treturn nil, err\n\t} else if vers != latestDataVersion {\n\t\treturn nil, errors.New(\"kv: data version is out of date. Run cayleyupgrade for your config to update the data\")\n\t}\n\tlist, err := qs.readIndexesMeta(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tqs.indexes.all = list\n\tqs.valueLRU = lru.New(2000)\n\tqs.exists.disabled, _ = opt.BoolKey(OptNoBloom, false)\n\tif err := qs.initBloomFilter(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif !qs.exists.disabled {\n\t\tif sz, err := qs.getSize(); err != nil {\n\t\t\treturn nil, err\n\t\t} else if sz == 0 {\n\t\t\tqs.mapBloom = make(map[string]*boom.BloomFilter)\n\t\t\tqs.mapNodes = boom.NewBloomFilter(100*1000*1000, 0.05)\n\t\t}\n\t}\n\treturn qs, nil\n}\n\nfunc setVersion(ctx context.Context, db kv.KV, version int64) error {\n\treturn kv.Update(ctx, db, func(tx kv.Tx) error {\n\t\tvar buf [8]byte\n\t\tbinary.LittleEndian.PutUint64(buf[:], uint64(version))\n\t\tif err := tx.Put(ctx, metaBucket.AppendBytes([]byte(\"version\")), buf[:]); err != nil {\n\t\t\treturn fmt.Errorf(\"couldn't write version: %v\", err)\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (qs *QuadStore) getMetaInt(ctx context.Context, key string) (int64, error) {\n\tvar v int64\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tval, err := tx.Get(ctx, metaBucket.AppendBytes([]byte(key)))\n\t\tif err == kv.ErrNotFound {\n\t\t\treturn ErrNoBucket\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tv, err = asInt64(val, 0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\treturn v, err\n}\n\nfunc (qs *QuadStore) getSize() (int64, error) {\n\tsz, err := qs.getMetaInt(context.TODO(), \"size\")\n\tif err == ErrNoBucket {\n\t\treturn 0, nil\n\t}\n\treturn sz, err\n}\n\nfunc (qs *QuadStore) Size() int64 {\n\tsz, _ := qs.getSize()\n\treturn sz\n}\n\nfunc (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\tsz, err := qs.getMetaInt(ctx, \"size\")\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\tst := graph.Stats{\n\t\tNodes: refs.Size{\n\t\t\tValue: sz / 3,\n\t\t\tExact: false, // TODO(dennwc): store nodes count\n\t\t},\n\t\tQuads: refs.Size{\n\t\t\tValue: sz,\n\t\t\tExact: true,\n\t\t},\n\t}\n\tif exact {\n\t\t// calculate the exact number of nodes\n\t\tst.Nodes.Value = 0\n\t\tit := qs.NodesAllIterator().Iterate()\n\t\tdefer it.Close()\n\t\tfor it.Next(ctx) {\n\t\t\tst.Nodes.Value++\n\t\t}\n\t\tif err := it.Err(); err != nil {\n\t\t\treturn st, err\n\t\t}\n\t\tst.Nodes.Exact = true\n\t}\n\treturn st, nil\n}\n\nfunc (qs *QuadStore) Close() error {\n\treturn qs.db.Close()\n}\n\nfunc (qs *QuadStore) getMetadata(ctx context.Context) (int64, error) {\n\tvar vers int64\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tval, err := tx.Get(ctx, metaBucket.AppendBytes([]byte(\"version\")))\n\t\tif err == kv.ErrNotFound {\n\t\t\treturn ErrNoBucket\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvers, err = asInt64(val, 0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\treturn vers, err\n}\n\nfunc asInt64(b []byte, empty int64) (int64, error) {\n\tif len(b) == 0 {\n\t\treturn empty, nil\n\t} else if len(b) != 8 {\n\t\treturn 0, fmt.Errorf(\"unexpected int size: %d\", len(b))\n\t}\n\tv := int64(binary.LittleEndian.Uint64(b))\n\treturn v, nil\n}\n\nfunc (qs *QuadStore) horizon(ctx context.Context) int64 {\n\th, _ := qs.getMetaInt(ctx, \"horizon\")\n\treturn h\n}\n\nfunc (qs *QuadStore) ValuesOf(ctx context.Context, vals []graph.Ref) ([]quad.Value, error) {\n\tout := make([]quad.Value, len(vals))\n\tvar (\n\t\tinds  []int\n\t\tirefs []uint64\n\t)\n\tfor i, v := range vals {\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t} else if pv, ok := v.(refs.PreFetchedValue); ok {\n\t\t\tout[i] = pv.NameOf()\n\t\t\tcontinue\n\t\t}\n\t\tswitch v := v.(type) {\n\t\tcase Int64Value:\n\t\t\tif v == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tinds = append(inds, i)\n\t\t\tirefs = append(irefs, uint64(v))\n\t\tdefault:\n\t\t\treturn out, fmt.Errorf(\"unknown type of graph.Ref; not meant for this quadstore. apparently a %#v\", v)\n\t\t}\n\t}\n\tif len(irefs) == 0 {\n\t\treturn out, nil\n\t}\n\tprim, err := qs.getPrimitives(ctx, irefs)\n\tif err != nil {\n\t\treturn out, err\n\t}\n\tvar last error\n\tfor i, p := range prim {\n\t\tif p == nil || !p.IsNode() {\n\t\t\tcontinue\n\t\t}\n\t\tqv, err := pquads.UnmarshalValue(p.Value)\n\t\tif err != nil {\n\t\t\tlast = err\n\t\t\tcontinue\n\t\t}\n\t\tout[inds[i]] = qv\n\t}\n\treturn out, last\n}\n\nfunc (qs *QuadStore) RefsOf(ctx context.Context, nodes []quad.Value) ([]graph.Ref, error) {\n\tvalues := make([]graph.Ref, len(nodes))\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tfor i, node := range nodes {\n\t\t\tvalue, err := qs.resolveQuadValue(ctx, tx, node)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvalues[i] = Int64Value(value)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn values, nil\n}\n\nfunc (qs *QuadStore) NameOf(v graph.Ref) (quad.Value, error) {\n\tctx := context.TODO()\n\tvals, err := qs.ValuesOf(ctx, []graph.Ref{v})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting NameOf %d: %w\", v, err)\n\t}\n\treturn vals[0], nil\n}\n\nfunc (qs *QuadStore) Quad(k graph.Ref) (quad.Quad, error) {\n\tkey, ok := k.(*proto.Primitive)\n\tif !ok {\n\t\treturn quad.Quad{}, fmt.Errorf(\"passed value was not a quad primitive: %T\", k)\n\t}\n\tctx := context.TODO()\n\tvar v quad.Quad\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tvar err error\n\t\tv, err = qs.primitiveToQuad(ctx, tx, key)\n\t\treturn err\n\t})\n\tif err == kv.ErrNotFound {\n\t\terr = nil\n\t}\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error fetching quad %#v: %w\", key, err)\n\t}\n\treturn v, err\n}\n\nfunc (qs *QuadStore) primitiveToQuad(ctx context.Context, tx kv.Tx, p *proto.Primitive) (quad.Quad, error) {\n\tq := &quad.Quad{}\n\tfor _, dir := range quad.Directions {\n\t\tv := p.GetDirection(dir)\n\t\tval, err := qs.getValFromLog(ctx, tx, v)\n\t\tif err != nil {\n\t\t\treturn *q, err\n\t\t}\n\t\tq.Set(dir, val)\n\t}\n\treturn *q, nil\n}\n\nfunc (qs *QuadStore) getValFromLog(ctx context.Context, tx kv.Tx, k uint64) (quad.Value, error) {\n\tif k == 0 {\n\t\treturn nil, nil\n\t}\n\tp, err := qs.getPrimitiveFromLog(ctx, tx, k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pquads.UnmarshalValue(p.Value)\n}\n\nfunc (qs *QuadStore) ValueOf(s quad.Value) (graph.Ref, error) {\n\tctx := context.TODO()\n\tvar out Int64Value\n\terr := kv.View(ctx, qs.db, func(tx kv.Tx) error {\n\t\tv, err := qs.resolveQuadValue(ctx, tx, s)\n\t\tout = Int64Value(v)\n\t\treturn err\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif out == 0 {\n\t\treturn nil, nil\n\t}\n\treturn out, nil\n}\n\nfunc (qs *QuadStore) QuadDirection(val graph.Ref, d quad.Direction) (graph.Ref, error) {\n\tp, ok := val.(*proto.Primitive)\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\tswitch d {\n\tcase quad.Subject:\n\t\treturn Int64Value(p.Subject), nil\n\tcase quad.Predicate:\n\t\treturn Int64Value(p.Predicate), nil\n\tcase quad.Object:\n\t\treturn Int64Value(p.Object), nil\n\tcase quad.Label:\n\t\tif p.Label == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn Int64Value(p.Label), nil\n\t}\n\treturn nil, nil\n}\n\nfunc (qs *QuadStore) getPrimitives(ctx context.Context, vals []uint64) ([]*proto.Primitive, error) {\n\ttx, err := qs.db.Tx(ctx, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer tx.Close()\n\ttx = wrapTx(tx)\n\treturn qs.getPrimitivesFromLog(ctx, tx, vals)\n}\n\ntype Int64Value uint64\n\nfunc (v Int64Value) Key() interface{} { return v }\n"
  },
  {
    "path": "graph/kv/quadstore_test.go",
    "content": "package kv_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\thenc \"encoding/hex\"\n\t\"fmt\"\n\t\"sort\"\n\t\"sync\"\n\t\"testing\"\n\n\thkv \"github.com/hidal-go/hidalgo/kv\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/kv\"\n\t\"github.com/cayleygraph/cayley/graph/kv/btree\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/writer\"\n)\n\nfunc hex(s string) []byte {\n\tb, err := henc.DecodeString(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn b\n}\n\nfunc irih(s string) []byte {\n\th := refs.HashOf(quad.IRI(s))\n\treturn h[:]\n}\n\nfunc irib(s string) string {\n\th := refs.HashOf(quad.IRI(s))\n\treturn string([]byte{'v', h[0], h[1]})\n}\n\nfunc iric(s string) string {\n\th := refs.HashOf(quad.IRI(s))\n\treturn string([]byte{'n', h[0], h[1]})\n}\n\nfunc key(b string, k []byte) hkv.Key {\n\treturn hkv.Key{[]byte(b), k}\n}\n\nfunc be(v ...uint64) []byte {\n\tb := make([]byte, 8*len(v))\n\tfor i, vi := range v {\n\t\tbinary.BigEndian.PutUint64(b[i*8:], vi)\n\t}\n\treturn b\n}\nfunc le(v uint64) []byte {\n\tvar b [8]byte\n\tbinary.LittleEndian.PutUint64(b[:], uint64(v))\n\treturn b[:]\n}\n\nconst (\n\tbMeta = \"meta\"\n\tbLog  = \"log\"\n)\n\nvar (\n\tkVers = []byte(\"version\")\n\tvVers = le(2)\n\n\tvAuto = []byte(\"auto\")\n\n\tkIndexes = []byte(\"indexes\")\n)\n\ntype Ops []kvOp\n\nfunc (s Ops) Len() int {\n\treturn len(s)\n}\n\nfunc (s Ops) Less(i, j int) bool {\n\ta, b := s[i], s[j]\n\treturn a.key.Compare(b.key) < 0\n}\n\nfunc (s Ops) Swap(i, j int) {\n\ts[i], s[j] = s[j], s[i]\n}\n\nfunc (s Ops) String() string {\n\tbuf := bytes.NewBuffer(nil)\n\tfor _, op := range s {\n\t\tse := \"\"\n\t\tif op.err != nil {\n\t\t\tse = \" (\" + op.err.Error() + \")\"\n\t\t}\n\t\tfmt.Fprintf(buf, \"%v: %q = %x%s\\n\", op.typ, op.key, op.val, se)\n\t}\n\treturn buf.String()\n}\n\nfunc TestApplyDeltas(t *testing.T) {\n\tkdb := btree.New()\n\n\thook := &kvHook{db: kdb}\n\texpect := func(exp Ops) {\n\t\tgot := hook.log()\n\t\tif len(exp) == len(got) {\n\t\t\tif false {\n\t\t\t\tsortByOp(exp, got)\n\t\t\t}\n\t\t\t// TODO: make node insert predictable\n\t\t\tfor i, d := range exp {\n\t\t\t\tif bytes.Equal(d.key[0], vAuto) {\n\t\t\t\t\texp[i].key = got[i].key\n\t\t\t\t}\n\t\t\t\tif bytes.Equal(d.val, vAuto) {\n\t\t\t\t\texp[i].val = got[i].val\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trequire.Equal(t, exp, got, \"%d\\n%v\\nvs\\n\\n%d\\n%v\", len(exp), exp, len(got), got)\n\t}\n\n\terr := kv.Init(hook, nil)\n\trequire.NoError(t, err)\n\n\texpect(Ops{\n\t\t{opGet, key(bMeta, kVers), nil, hkv.ErrNotFound},\n\t\t{opPut, key(bMeta, []byte{}), nil, nil},\n\t\t{opPut, key(bLog, []byte{}), nil, nil},\n\t\t{opPut, key(\"sp\", []byte{}), nil, nil},\n\t\t{opPut, key(\"ops\", []byte{}), nil, nil},\n\t\t{opPut, key(bMeta, kVers), vVers, nil},\n\t\t{opPut, key(bMeta, kIndexes), []byte(`[{\"dirs\":\"AQI=\",\"unique\":false},{\"dirs\":\"AwIB\",\"unique\":false}]`), nil},\n\t})\n\n\tqs, err := kv.New(hook, nil)\n\trequire.NoError(t, err)\n\tdefer qs.Close()\n\n\texpect(Ops{\n\t\t{opGet, key(bMeta, kVers), vVers, nil},\n\t\t{opGet, key(bMeta, kIndexes), []byte(`[{\"dirs\":\"AQI=\",\"unique\":false},{\"dirs\":\"AwIB\",\"unique\":false}]`), nil},\n\t\t{opGet, key(bMeta, []byte(\"size\")), nil, hkv.ErrNotFound},\n\t})\n\n\tqw, err := writer.NewSingle(qs, graph.IgnoreOpts{})\n\trequire.NoError(t, err)\n\n\terr = qw.AddQuad(quad.MakeIRI(\"a\", \"b\", \"c\", \"\"))\n\trequire.NoError(t, err)\n\n\texpect(Ops{\n\t\t{opGet, key(bMeta, []byte(\"horizon\")), nil, hkv.ErrNotFound},\n\t\t{opPut, key(bMeta, []byte(\"horizon\")), le(3), nil},\n\n\t\t{opPut, key(irib(\"a\"), irih(\"a\")), vAuto, nil},\n\t\t{opPut, key(bLog, be(1)), vAuto, nil},\n\t\t{opPut, key(irib(\"b\"), irih(\"b\")), vAuto, nil},\n\t\t{opPut, key(bLog, be(2)), vAuto, nil},\n\t\t{opPut, key(irib(\"c\"), irih(\"c\")), vAuto, nil},\n\t\t{opPut, key(bLog, be(3)), vAuto, nil},\n\n\t\t{opPut, key(iric(\"a\"), irih(\"a\")), hex(\"01\"), nil},\n\t\t{opPut, key(iric(\"b\"), irih(\"b\")), hex(\"01\"), nil},\n\t\t{opPut, key(iric(\"c\"), irih(\"c\")), hex(\"01\"), nil},\n\t\t{opGet, key(bMeta, []byte(\"horizon\")), le(3), nil},\n\t\t{opPut, key(bMeta, []byte(\"horizon\")), le(4), nil},\n\t\t{opPut, key(bLog, be(4)), vAuto, nil},\n\t\t{opGet, key(bMeta, []byte(\"size\")), nil, hkv.ErrNotFound},\n\t\t{opPut, key(bMeta, []byte(\"size\")), le(1), nil},\n\t\t{opPut, key(\"ops\", be(3, 2, 1)), hex(\"04\"), nil},\n\t\t{opPut, key(\"sp\", be(1, 2)), hex(\"04\"), nil},\n\t})\n\n\terr = qw.AddQuad(quad.MakeIRI(\"a\", \"b\", \"e\", \"\"))\n\trequire.NoError(t, err)\n\n\texpect(Ops{\n\t\t// served from IRI cache\n\t\t//{opGet, irib(\"a\"), irih(\"a\"), vAuto, nil},\n\t\t//{opGet, irib(\"b\"), irih(\"b\"), vAuto, nil},\n\t\t{opGet, key(bMeta, []byte(\"horizon\")), le(4), nil},\n\t\t{opPut, key(bMeta, []byte(\"horizon\")), le(5), nil},\n\n\t\t{opPut, key(irib(\"e\"), irih(\"e\")), vAuto, nil},\n\t\t{opPut, key(bLog, be(5)), vAuto, nil},\n\n\t\t{opGet, key(iric(\"a\"), irih(\"a\")), hex(\"01\"), nil},\n\t\t{opGet, key(iric(\"b\"), irih(\"b\")), hex(\"01\"), nil},\n\t\t{opPut, key(iric(\"a\"), irih(\"a\")), hex(\"02\"), nil},\n\t\t{opPut, key(iric(\"b\"), irih(\"b\")), hex(\"02\"), nil},\n\t\t{opPut, key(iric(\"e\"), irih(\"e\")), hex(\"01\"), nil},\n\t\t{opGet, key(bMeta, []byte(\"horizon\")), le(5), nil},\n\t\t{opPut, key(bMeta, []byte(\"horizon\")), le(6), nil},\n\t\t{opPut, key(bLog, be(6)), vAuto, nil},\n\t\t{opGet, key(bMeta, []byte(\"size\")), le(1), nil},\n\t\t{opPut, key(bMeta, []byte(\"size\")), le(2), nil},\n\t\t{opPut, key(\"ops\", be(5, 2, 1)), hex(\"06\"), nil},\n\t\t{opGet, key(\"sp\", be(1, 2)), hex(\"04\"), nil},\n\t\t{opPut, key(\"sp\", be(1, 2)), hex(\"0406\"), nil},\n\t})\n\n\terr = qw.RemoveQuad(quad.MakeIRI(\"a\", \"b\", \"c\", \"\"))\n\texpect(Ops{\n\t\t{opGet, key(\"sp\", be(1, 2)), hex(\"0406\"), nil},\n\t\t{opGet, key(\"ops\", be(3, 2, 1)), hex(\"04\"), nil},\n\t\t{opGet, key(bLog, be(4)), vAuto, nil},\n\t\t{opPut, key(bLog, be(4)), vAuto, nil},\n\t\t{opGet, key(bMeta, []byte(\"size\")), le(2), nil},\n\t\t{opPut, key(bMeta, []byte(\"size\")), le(1), nil},\n\t\t{opGet, key(iric(\"a\"), irih(\"a\")), hex(\"02\"), nil},\n\t\t{opGet, key(iric(\"b\"), irih(\"b\")), hex(\"02\"), nil},\n\t\t{opGet, key(iric(\"c\"), irih(\"c\")), hex(\"01\"), nil},\n\t\t{opPut, key(iric(\"a\"), irih(\"a\")), hex(\"01\"), nil},\n\t\t{opPut, key(iric(\"b\"), irih(\"b\")), hex(\"01\"), nil},\n\t\t{opDel, key(iric(\"c\"), irih(\"c\")), nil, nil},\n\t\t{opDel, key(irib(\"c\"), irih(\"c\")), nil, nil},\n\t\t{opDel, key(bLog, be(3)), nil, nil},\n\t})\n\trequire.NoError(t, err)\n}\n\nfunc clone(b []byte) []byte {\n\tif b == nil {\n\t\treturn nil\n\t}\n\treturn append([]byte{}, b...)\n}\n\nfunc sortByOp(exp, got Ops) {\n\t// sort ops of one type\n\tli := -1\n\ttyp, b := -1, \"\"\n\tcheck := func(i int) {\n\t\tif li < 0 || i-li <= 0 {\n\t\t\treturn\n\t\t}\n\t\tsort.Sort(exp[li:i])\n\t\tsort.Sort(got[li:i])\n\t\t//sort.Sort(bothOps{a: exp[li:i], b: got[li:i]})\n\t\tli, typ, b = -1, -1, \"\"\n\t}\n\tfor i, op := range exp {\n\t\tif op.typ != typ {\n\t\t\tcheck(i)\n\t\t}\n\t\tif li < 0 {\n\t\t\tli, typ, b = i, op.typ, string(op.key[0])\n\t\t}\n\t}\n\t_ = b\n\tcheck(len(exp))\n}\n\nconst (\n\topGet = iota\n\topPut\n\topDel\n)\n\ntype kvOp struct {\n\ttyp int\n\tkey hkv.Key\n\tval hkv.Value\n\terr error\n}\n\nvar _ hkv.KV = (*kvHook)(nil)\n\ntype kvHook struct {\n\tdb hkv.KV\n\n\tmu  sync.Mutex\n\tops Ops\n}\n\nfunc (h *kvHook) log() Ops {\n\th.mu.Lock()\n\tops := h.ops\n\th.ops = nil\n\th.mu.Unlock()\n\treturn ops\n}\n\nfunc (h *kvHook) addOp(op kvOp) {\n\th.mu.Lock()\n\th.ops = append(h.ops, op)\n\th.mu.Unlock()\n}\n\nfunc (h *kvHook) Close() error {\n\treturn h.db.Close()\n}\n\nfunc (h *kvHook) Tx(ctx context.Context, rw bool) (hkv.Tx, error) {\n\ttx, err := h.db.Tx(ctx, rw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn txHook{h: h, tx: tx}, nil\n}\n\nfunc (h *kvHook) View(ctx context.Context, fn func(tx hkv.Tx) error) error {\n\treturn h.db.View(ctx, func(tx hkv.Tx) error {\n\t\treturn fn(txHook{h: h, tx: tx})\n\t})\n}\n\nfunc (h *kvHook) Update(ctx context.Context, fn func(tx hkv.Tx) error) error {\n\treturn h.db.Update(ctx, func(tx hkv.Tx) error {\n\t\treturn fn(txHook{h: h, tx: tx})\n\t})\n}\n\ntype txHook struct {\n\th  *kvHook\n\ttx hkv.Tx\n}\n\nfunc (h txHook) Commit(ctx context.Context) error {\n\treturn h.tx.Commit(ctx)\n}\n\nfunc (h txHook) Close() error {\n\treturn h.tx.Close()\n}\n\nfunc (h txHook) GetBatch(ctx context.Context, keys []hkv.Key) ([]hkv.Value, error) {\n\tvals, err := h.tx.GetBatch(ctx, keys)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor i, k := range keys {\n\t\th.h.addOp(kvOp{\n\t\t\tkey: k.Clone(),\n\t\t\tval: vals[i].Clone(),\n\t\t})\n\t}\n\treturn vals, nil\n}\n\nfunc (h txHook) Get(ctx context.Context, k hkv.Key) (hkv.Value, error) {\n\tv, err := h.tx.Get(ctx, k)\n\th.h.addOp(kvOp{\n\t\tkey: k.Clone(),\n\t\tval: v.Clone(),\n\t\terr: err,\n\t})\n\treturn v, err\n}\n\nfunc (h txHook) Put(ctx context.Context, k hkv.Key, v hkv.Value) error {\n\terr := h.tx.Put(ctx, k, v)\n\th.h.addOp(kvOp{\n\t\ttyp: opPut,\n\t\tkey: k.Clone(),\n\t\tval: v.Clone(),\n\t\terr: err,\n\t})\n\treturn err\n}\n\nfunc (h txHook) Del(ctx context.Context, k hkv.Key) error {\n\terr := h.tx.Del(ctx, k)\n\th.h.addOp(kvOp{\n\t\ttyp: opDel,\n\t\tkey: k.Clone(),\n\t\terr: err,\n\t})\n\treturn err\n}\n\nfunc (h txHook) Scan(ctx context.Context, opts ...hkv.IteratorOption) hkv.Iterator {\n\treturn h.tx.Scan(ctx, opts...)\n}\n"
  },
  {
    "path": "graph/kv/registry.go",
    "content": "package kv\n\nimport (\n\t\"strings\"\n\n\t\"github.com/hidal-go/hidalgo/kv\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n)\n\nfunc init() {\n\tfor _, r := range kv.List() {\n\t\tswitch r.Name {\n\t\tcase \"bolt\", \"bbolt\":\n\t\t\tcontinue // legacy: register manually; see comments in the bolt package\n\t\t}\n\t\tr := r\n\t\treg := Registration{\n\t\t\tInitFunc: func(s string, options graph.Options) (kv.KV, error) {\n\t\t\t\treturn r.OpenPath(s)\n\t\t\t},\n\t\t\tNewFunc: func(s string, options graph.Options) (kv.KV, error) {\n\t\t\t\treturn r.OpenPath(s)\n\t\t\t},\n\t\t\tIsPersistent: !r.Volatile,\n\t\t}\n\t\tname := r.Name\n\t\t// override names for backward compatibility\n\t\t// names are also nicer without the \"flat.\" prefix\n\t\tif strings.HasPrefix(name, \"flat.\") && !graph.IsRegistered(name[5:]) {\n\t\t\tname = name[5:]\n\t\t}\n\t\tRegister(name, reg)\n\t}\n}\n"
  },
  {
    "path": "graph/linksto.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\n// Defines one of the base iterators, the LinksTo iterator. A LinksTo takes a\n// subiterator of nodes, and contains an iteration of links which \"link to\"\n// those nodes in a given direction.\n//\n// Next()ing a LinksTo is straightforward -- iterate through all links to //\n// things in the subiterator, and then advance the subiterator, and do it again.\n// LinksTo is therefore sensitive to growing with a fanout. (A small-sized\n// subiterator could cause LinksTo to be large).\n//\n// Contains()ing a LinksTo means, given a link, take the direction we care about\n// and check if it's in our subiterator. Checking is therefore fairly cheap, and\n// similar to checking the subiterator alone.\n//\n// Can be seen as the dual of the HasA iterator.\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// A LinksTo has a reference back to the graph.QuadStore (to create the iterators\n// for each node) the subiterator, and the direction the iterator comes from.\n// `next_it` is the tempoarary iterator held per result in `primary_it`.\ntype LinksTo struct {\n\tqs      QuadIndexer\n\tprimary iterator.Shape\n\tdir     quad.Direction\n\tsize    refs.Size\n}\n\n// NewLinksTo construct a new LinksTo iterator around a direction and a subiterator of\n// nodes.\nfunc NewLinksTo(qs QuadIndexer, it iterator.Shape, d quad.Direction) *LinksTo {\n\treturn &LinksTo{\n\t\tqs:      qs,\n\t\tprimary: it,\n\t\tdir:     d,\n\t}\n}\n\n// Direction returns the direction under consideration.\nfunc (it *LinksTo) Direction() quad.Direction { return it.dir }\n\nfunc (it *LinksTo) Iterate() iterator.Scanner {\n\treturn newLinksToNext(it.qs, it.primary.Iterate(), it.dir)\n}\n\nfunc (it *LinksTo) Lookup() iterator.Index {\n\treturn newLinksToContains(it.qs, it.primary.Lookup(), it.dir)\n}\n\nfunc (it *LinksTo) String() string {\n\treturn fmt.Sprintf(\"LinksTo(%v)\", it.dir)\n}\n\n// SubIterators returns a list containing only our subiterator.\nfunc (it *LinksTo) SubIterators() []iterator.Shape {\n\treturn []iterator.Shape{it.primary}\n}\n\n// Optimize the LinksTo, by replacing it if it can be.\nfunc (it *LinksTo) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\tnewPrimary, changed := it.primary.Optimize(ctx)\n\tif changed {\n\t\tit.primary = newPrimary\n\t\tif iterator.IsNull(it.primary) {\n\t\t\treturn it.primary, true\n\t\t}\n\t}\n\treturn it, false\n}\n\n// Stats returns a guess as to how big or costly it is to next the iterator.\nfunc (it *LinksTo) Stats(ctx context.Context) (iterator.Costs, error) {\n\tsubitStats, err := it.primary.Stats(ctx)\n\t// TODO(barakmich): These should really come from the quadstore itself\n\tcheckConstant := int64(1)\n\tnextConstant := int64(2)\n\treturn iterator.Costs{\n\t\tNextCost:     nextConstant + subitStats.NextCost,\n\t\tContainsCost: checkConstant + subitStats.ContainsCost,\n\t\tSize:         it.getSize(ctx),\n\t}, err\n}\n\nfunc (it *LinksTo) getSize(ctx context.Context) refs.Size {\n\tif it.size.Value != 0 {\n\t\treturn it.size\n\t}\n\tif fixed, ok := it.primary.(*iterator.Fixed); ok {\n\t\t// get real sizes from sub iterators\n\t\tvar (\n\t\t\tsz    int64\n\t\t\texact = true\n\t\t)\n\t\tfor _, v := range fixed.Values() {\n\t\t\tsit := it.qs.QuadIterator(it.dir, v)\n\t\t\tst, _ := sit.Stats(ctx)\n\t\t\tsz += st.Size.Value\n\t\t\texact = exact && st.Size.Exact\n\t\t}\n\t\tit.size.Value, it.size.Exact = sz, exact\n\t\treturn it.size\n\t}\n\tstats, _ := it.qs.Stats(ctx, false)\n\tmaxSize := stats.Quads.Value/2 + 1\n\t// TODO(barakmich): It should really come from the quadstore itself\n\tconst fanoutFactor = 20\n\tst, _ := it.primary.Stats(ctx)\n\tvalue := st.Size.Value * fanoutFactor\n\tif value > maxSize {\n\t\tvalue = maxSize\n\t}\n\tit.size.Value, it.size.Exact = value, false\n\treturn it.size\n}\n\n// A LinksTo has a reference back to the graph.QuadStore (to create the iterators\n// for each node) the subiterator, and the direction the iterator comes from.\n// `next_it` is the tempoarary iterator held per result in `primary_it`.\ntype linksToNext struct {\n\tqs      QuadIndexer\n\tprimary iterator.Scanner\n\tdir     quad.Direction\n\tnextIt  iterator.Scanner\n\tresult  refs.Ref\n\terr     error\n}\n\n// Construct a new LinksTo iterator around a direction and a subiterator of\n// nodes.\nfunc newLinksToNext(qs QuadIndexer, it iterator.Scanner, d quad.Direction) iterator.Scanner {\n\treturn &linksToNext{\n\t\tqs:      qs,\n\t\tprimary: it,\n\t\tdir:     d,\n\t\tnextIt:  iterator.NewNull().Iterate(),\n\t}\n}\n\n// Return the direction under consideration.\nfunc (it *linksToNext) Direction() quad.Direction { return it.dir }\n\n// Tag these results, and our subiterator's results.\nfunc (it *linksToNext) TagResults(dst map[string]refs.Ref) {\n\tit.primary.TagResults(dst)\n}\n\nfunc (it *linksToNext) String() string {\n\treturn fmt.Sprintf(\"LinksToNext(%v)\", it.dir)\n}\n\n// Next()ing a LinksTo operates as described above.\nfunc (it *linksToNext) Next(ctx context.Context) bool {\n\tfor {\n\t\tif it.nextIt.Next(ctx) {\n\t\t\tit.result = it.nextIt.Result()\n\t\t\treturn true\n\t\t}\n\n\t\t// If there's an error in the 'next' iterator, we save it and we're done.\n\t\tit.err = it.nextIt.Err()\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\t// Subiterator is empty, get another one\n\t\tif !it.primary.Next(ctx) {\n\t\t\t// Possibly save error\n\t\t\tit.err = it.primary.Err()\n\n\t\t\t// We're out of nodes in our subiterator, so we're done as well.\n\t\t\treturn false\n\t\t}\n\t\tit.nextIt.Close()\n\t\tit.nextIt = it.qs.QuadIterator(it.dir, it.primary.Result()).Iterate()\n\n\t\t// Continue -- return the first in the next set.\n\t}\n}\n\nfunc (it *linksToNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *linksToNext) Result() refs.Ref {\n\treturn it.result\n}\n\n// Close closes the iterator.  It closes all subiterators it can, but\n// returns the first error it encounters.\nfunc (it *linksToNext) Close() error {\n\terr := it.nextIt.Close()\n\n\t_err := it.primary.Close()\n\tif _err != nil && err == nil {\n\t\terr = _err\n\t}\n\n\treturn err\n}\n\n// We won't ever have a new result, but our subiterators might.\nfunc (it *linksToNext) NextPath(ctx context.Context) bool {\n\tok := it.primary.NextPath(ctx)\n\tif !ok {\n\t\tit.err = it.primary.Err()\n\t}\n\treturn ok\n}\n\n// A LinksTo has a reference back to the graph.QuadStore (to create the iterators\n// for each node) the subiterator, and the direction the iterator comes from.\n// `next_it` is the tempoarary iterator held per result in `primary_it`.\ntype linksToContains struct {\n\tqs      QuadIndexer\n\tprimary iterator.Index\n\tdir     quad.Direction\n\tresult  refs.Ref\n\terr     error\n}\n\n// Construct a new LinksTo iterator around a direction and a subiterator of\n// nodes.\nfunc newLinksToContains(qs QuadIndexer, it iterator.Index, d quad.Direction) iterator.Index {\n\treturn &linksToContains{\n\t\tqs:      qs,\n\t\tprimary: it,\n\t\tdir:     d,\n\t}\n}\n\n// Return the direction under consideration.\nfunc (it *linksToContains) Direction() quad.Direction { return it.dir }\n\n// Tag these results, and our subiterator's results.\nfunc (it *linksToContains) TagResults(dst map[string]refs.Ref) {\n\tit.primary.TagResults(dst)\n}\n\nfunc (it *linksToContains) String() string {\n\treturn fmt.Sprintf(\"LinksToContains(%v)\", it.dir)\n}\n\n// If it checks in the right direction for the subiterator, it is a valid link\n// for the LinksTo.\nfunc (it *linksToContains) Contains(ctx context.Context, val refs.Ref) bool {\n\tnode, err := it.qs.QuadDirection(val, it.dir)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tif it.primary.Contains(ctx, node) {\n\t\tit.result = val\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *linksToContains) Err() error {\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\treturn it.primary.Err()\n}\n\nfunc (it *linksToContains) Result() refs.Ref {\n\treturn it.result\n}\n\n// Close closes the iterator.  It closes all subiterators it can, but\n// returns the first error it encounters.\nfunc (it *linksToContains) Close() error {\n\treturn it.primary.Close()\n}\n\n// We won't ever have a new result, but our subiterators might.\nfunc (it *linksToContains) NextPath(ctx context.Context) bool {\n\treturn it.primary.NextPath(ctx)\n}\n"
  },
  {
    "path": "graph/linksto_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphmock\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc TestLinksTo(t *testing.T) {\n\tctx := context.TODO()\n\tobject := quad.Raw(\"cool\")\n\tq := quad.Quad{Subject: quad.IRI(\"alice\"), Predicate: quad.IRI(\"is\"), Object: object, Label: nil}\n\tqs := &graphmock.Store{\n\t\tData: []quad.Quad{q},\n\t}\n\tfixed := iterator.NewFixed()\n\n\tval, err := qs.ValueOf(object)\n\trequire.NoError(t, err)\n\n\tfixed.Add(val)\n\tlto := graph.NewLinksTo(qs, fixed, quad.Object).Iterate()\n\trequire.True(t, lto.Next(ctx))\n\tqv, err := qs.Quad(lto.Result())\n\trequire.NoError(t, err)\n\trequire.Equal(t, q, qv)\n}\n"
  },
  {
    "path": "graph/log/graphlog.go",
    "content": "package graphlog\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype Op interface {\n\tisOp()\n}\n\nvar (\n\t_ Op = NodeUpdate{}\n\t_ Op = QuadUpdate{}\n)\n\ntype NodeUpdate struct {\n\tHash   refs.ValueHash\n\tVal    quad.Value\n\tRefInc int\n}\n\nfunc (NodeUpdate) isOp() {}\n\ntype QuadUpdate struct {\n\tInd  int\n\tQuad refs.QuadHash\n\tDel  bool\n}\n\nfunc (QuadUpdate) isOp() {}\n\ntype Deltas struct {\n\tIncNode []NodeUpdate\n\tDecNode []NodeUpdate\n\tQuadAdd []QuadUpdate\n\tQuadDel []QuadUpdate\n}\n\nfunc InsertQuads(in []quad.Quad) *Deltas {\n\thnodes := make(map[refs.ValueHash]*NodeUpdate, len(in)*2)\n\tquadAdd := make([]QuadUpdate, 0, len(in))\n\tfor i, qd := range in {\n\t\tvar q refs.QuadHash\n\t\tfor _, dir := range quad.Directions {\n\t\t\tv := qd.Get(dir)\n\t\t\tif v == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\th := refs.HashOf(v)\n\t\t\tq.Set(dir, h)\n\t\t\tn := hnodes[h]\n\t\t\tif n == nil {\n\t\t\t\tn = &NodeUpdate{Hash: h, Val: v}\n\t\t\t\thnodes[h] = n\n\t\t\t}\n\t\t\tn.RefInc++\n\t\t}\n\t\tquadAdd = append(quadAdd, QuadUpdate{Ind: i, Quad: q})\n\t}\n\tincNodes := make([]NodeUpdate, 0, len(hnodes))\n\tfor _, n := range hnodes {\n\t\tincNodes = append(incNodes, *n)\n\t}\n\thnodes = nil\n\tsort.Slice(incNodes, func(i, j int) bool {\n\t\treturn bytes.Compare(incNodes[i].Hash[:], incNodes[j].Hash[:]) < 0\n\t})\n\treturn &Deltas{\n\t\tIncNode: incNodes,\n\t\tQuadAdd: quadAdd,\n\t}\n}\n\nfunc SplitDeltas(in []graph.Delta) *Deltas {\n\thnodes := make(map[refs.ValueHash]*NodeUpdate, len(in)*2)\n\tquadAdd := make([]QuadUpdate, 0, len(in))\n\tquadDel := make([]QuadUpdate, 0, len(in)/2)\n\tvar nadd, ndel int\n\tfor i, d := range in {\n\t\tdn := 0\n\t\tswitch d.Action {\n\t\tcase graph.Add:\n\t\t\tdn = +1\n\t\t\tnadd++\n\t\tcase graph.Delete:\n\t\t\tdn = -1\n\t\t\tndel++\n\t\tdefault:\n\t\t\tpanic(\"unknown action\")\n\t\t}\n\t\tvar q refs.QuadHash\n\t\tfor _, dir := range quad.Directions {\n\t\t\tv := d.Quad.Get(dir)\n\t\t\tif v == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\th := refs.HashOf(v)\n\t\t\tq.Set(dir, h)\n\t\t\tn := hnodes[h]\n\t\t\tif n == nil {\n\t\t\t\tn = &NodeUpdate{Hash: h, Val: v}\n\t\t\t\thnodes[h] = n\n\t\t\t}\n\t\t\tn.RefInc += dn\n\t\t}\n\t\tu := QuadUpdate{Ind: i, Quad: q, Del: d.Action == graph.Delete}\n\t\tif !u.Del {\n\t\t\tquadAdd = append(quadAdd, u)\n\t\t} else {\n\t\t\tquadDel = append(quadDel, u)\n\t\t}\n\t}\n\tincNodes := make([]NodeUpdate, 0, nadd)\n\tdecNodes := make([]NodeUpdate, 0, ndel)\n\tfor _, n := range hnodes {\n\t\tif n.RefInc >= 0 {\n\t\t\tincNodes = append(incNodes, *n)\n\t\t} else {\n\t\t\tdecNodes = append(decNodes, *n)\n\t\t}\n\t}\n\tsort.Slice(incNodes, func(i, j int) bool {\n\t\treturn bytes.Compare(incNodes[i].Hash[:], incNodes[j].Hash[:]) < 0\n\t})\n\tsort.Slice(decNodes, func(i, j int) bool {\n\t\treturn bytes.Compare(decNodes[i].Hash[:], decNodes[j].Hash[:]) < 0\n\t})\n\thnodes = nil\n\treturn &Deltas{\n\t\tIncNode: incNodes, DecNode: decNodes,\n\t\tQuadAdd: quadAdd, QuadDel: quadDel,\n\t}\n}\n"
  },
  {
    "path": "graph/memstore/Makefile",
    "content": "# Copyright 2014 The Cayley Authors. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n.PHONY: specify \n\n# Do not commit changes to this line unless you are satisfied\n# that the github.com/cznic/b tests AND the cayley integration\n# tests pass with the new sha.\npinned=82d9e96a4503a42315b0fdf5201314302beafe06\n\nspecify:\n\trm -rf b\n\tgit clone https://github.com/cznic/b\n\tcd b && git checkout $(pinned)\n\tgo test ./b\n\t@sed -e 's|interface{}[^{]*/\\*K\\*/|int64|g' -e 's|interface{}[^{]*/\\*V\\*/|\\*primitive|g' b/btree.go >keys.go\n\trm -rf b\n"
  },
  {
    "path": "graph/memstore/all_iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage memstore\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nvar _ iterator.Shape = (*allIterator)(nil)\n\ntype allIterator struct {\n\tqs    *QuadStore\n\tall   []*Primitive\n\tmaxid int64 // id of last observed insert (prim id)\n\tnodes bool\n}\n\nfunc (qs *QuadStore) newAllIterator(nodes bool, maxid int64) *allIterator {\n\treturn &allIterator{\n\t\tqs: qs, all: qs.cloneAll(), nodes: nodes,\n\t\tmaxid: maxid,\n\t}\n}\n\nfunc (it *allIterator) Iterate() iterator.Scanner {\n\treturn it.qs.newAllIteratorNext(it.nodes, it.maxid, it.all)\n}\n\nfunc (it *allIterator) Lookup() iterator.Index {\n\treturn it.qs.newAllIteratorContains(it.nodes, it.maxid)\n}\n\nfunc (it *allIterator) SubIterators() []iterator.Shape { return nil }\nfunc (it *allIterator) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\treturn it, false\n}\n\nfunc (it *allIterator) String() string {\n\treturn \"MemStoreAll\"\n}\n\nfunc (it *allIterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\treturn iterator.Costs{\n\t\tNextCost:     1,\n\t\tContainsCost: 1,\n\t\tSize: refs.Size{\n\t\t\t// TODO(dennwc): use maxid?\n\t\t\tValue: int64(len(it.all)),\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\nfunc (p *Primitive) filter(isNode bool, maxid int64) bool {\n\tif p.ID > maxid {\n\t\treturn false\n\t} else if isNode && p.Value != nil {\n\t\treturn true\n\t} else if !isNode && !p.Quad.Zero() {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype allIteratorNext struct {\n\tqs    *QuadStore\n\tall   []*Primitive\n\tmaxid int64 // id of last observed insert (prim id)\n\tnodes bool\n\n\ti    int // index into qs.all\n\tcur  *Primitive\n\tdone bool\n}\n\nfunc (qs *QuadStore) newAllIteratorNext(nodes bool, maxid int64, all []*Primitive) *allIteratorNext {\n\treturn &allIteratorNext{\n\t\tqs: qs, all: all, nodes: nodes,\n\t\ti: -1, maxid: maxid,\n\t}\n}\n\nfunc (it *allIteratorNext) ok(p *Primitive) bool {\n\treturn p.filter(it.nodes, it.maxid)\n}\n\nfunc (it *allIteratorNext) Next(ctx context.Context) bool {\n\tit.cur = nil\n\tif it.done {\n\t\treturn false\n\t}\n\tall := it.all\n\tif it.i >= len(all) {\n\t\tit.done = true\n\t\treturn false\n\t}\n\tit.i++\n\tfor ; it.i < len(all); it.i++ {\n\t\tp := all[it.i]\n\t\tif p.ID > it.maxid {\n\t\t\tbreak\n\t\t}\n\t\tif it.ok(p) {\n\t\t\tit.cur = p\n\t\t\treturn true\n\t\t}\n\t}\n\tit.done = true\n\treturn false\n}\n\nfunc (it *allIteratorNext) Result() graph.Ref {\n\tif it.cur == nil {\n\t\treturn nil\n\t}\n\tif !it.cur.Quad.Zero() {\n\t\treturn qprim{p: it.cur}\n\t}\n\treturn bnode(it.cur.ID)\n}\n\nfunc (it *allIteratorNext) Err() error { return nil }\nfunc (it *allIteratorNext) Close() error {\n\tit.done = true\n\tit.all = nil\n\treturn nil\n}\n\nfunc (it *allIteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *allIteratorNext) String() string {\n\treturn \"MemStoreAllNext\"\n}\nfunc (it *allIteratorNext) NextPath(ctx context.Context) bool { return false }\n\ntype allIteratorContains struct {\n\tqs    *QuadStore\n\tmaxid int64 // id of last observed insert (prim id)\n\tnodes bool\n\n\tcur  *Primitive\n\tdone bool\n}\n\nfunc (qs *QuadStore) newAllIteratorContains(nodes bool, maxid int64) *allIteratorContains {\n\treturn &allIteratorContains{\n\t\tqs: qs, nodes: nodes,\n\t\tmaxid: maxid,\n\t}\n}\n\nfunc (it *allIteratorContains) ok(p *Primitive) bool {\n\treturn p.filter(it.nodes, it.maxid)\n}\n\nfunc (it *allIteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tit.cur = nil\n\tif it.done {\n\t\treturn false\n\t}\n\tid, ok := asID(v)\n\tif !ok {\n\t\treturn false\n\t}\n\tp := it.qs.prim[id]\n\tif p.ID > it.maxid {\n\t\treturn false\n\t}\n\tif !it.ok(p) {\n\t\treturn false\n\t}\n\tit.cur = p\n\treturn true\n}\nfunc (it *allIteratorContains) Result() graph.Ref {\n\tif it.cur == nil {\n\t\treturn nil\n\t}\n\tif !it.cur.Quad.Zero() {\n\t\treturn qprim{p: it.cur}\n\t}\n\treturn bnode(it.cur.ID)\n}\n\nfunc (it *allIteratorContains) Err() error { return nil }\nfunc (it *allIteratorContains) Close() error {\n\tit.done = true\n\treturn nil\n}\n\nfunc (it *allIteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *allIteratorContains) String() string {\n\treturn \"MemStoreAllContains\"\n}\nfunc (it *allIteratorContains) NextPath(ctx context.Context) bool { return false }\n"
  },
  {
    "path": "graph/memstore/gen.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//go:generate make specify\n\npackage memstore\n"
  },
  {
    "path": "graph/memstore/iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage memstore\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype Iterator struct {\n\tqs    *QuadStore\n\ttree  *Tree\n\td     quad.Direction\n\tvalue int64\n}\n\nfunc (qs *QuadStore) newIterator(tree *Tree, d quad.Direction, value int64) *Iterator {\n\treturn &Iterator{\n\t\tqs:    qs,\n\t\ttree:  tree,\n\t\td:     d,\n\t\tvalue: value,\n\t}\n}\n\nfunc (it *Iterator) Iterate() iterator.Scanner {\n\t// TODO(dennwc): it doesn't check the direction and value, while Contains does; is it expected?\n\treturn it.qs.newIteratorNext(it.tree, it.d)\n}\n\nfunc (it *Iterator) Lookup() iterator.Index {\n\treturn it.qs.newIteratorContains(it.tree, it.d, it.value)\n}\n\nfunc (it *Iterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *Iterator) String() string {\n\treturn fmt.Sprintf(\"MemStore(%v)\", it.d)\n}\n\nfunc (it *Iterator) Sorted() bool { return true }\n\nfunc (it *Iterator) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\treturn it, false\n}\n\nfunc (it *Iterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\treturn iterator.Costs{\n\t\tContainsCost: int64(math.Log(float64(it.tree.Len()))) + 1,\n\t\tNextCost:     1,\n\t\tSize: refs.Size{\n\t\t\tValue: int64(it.tree.Len()),\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\ntype iteratorNext struct {\n\tnodes bool\n\tqs    *QuadStore\n\ttree  *Tree\n\td     quad.Direction\n\n\titer *Enumerator\n\tcur  *Primitive\n\terr  error\n}\n\nfunc (qs *QuadStore) newIteratorNext(tree *Tree, d quad.Direction) *iteratorNext {\n\treturn &iteratorNext{\n\t\tnodes: d == 0,\n\t\td:     d,\n\t\tqs:    qs,\n\t\ttree:  tree,\n\t}\n}\n\nfunc (it *iteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorNext) Close() error {\n\treturn nil\n}\n\nfunc (it *iteratorNext) Next(ctx context.Context) bool {\n\tif it.iter == nil {\n\t\tit.iter, it.err = it.tree.SeekFirst()\n\t\tif it.err == io.EOF || it.iter == nil {\n\t\t\tit.err = nil\n\t\t\treturn false\n\t\t} else if it.err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\tfor {\n\t\t_, p, err := it.iter.Next()\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tit.err = err\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t\tit.cur = p\n\t\treturn true\n\t}\n}\n\nfunc (it *iteratorNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorNext) Result() graph.Ref {\n\tif it.cur == nil {\n\t\treturn nil\n\t}\n\treturn qprim{p: it.cur}\n}\n\nfunc (it *iteratorNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorNext) String() string {\n\treturn fmt.Sprintf(\"MemStoreNext(%v)\", it.d)\n}\n\nfunc (it *iteratorNext) Sorted() bool { return true }\n\ntype iteratorContains struct {\n\tnodes bool\n\tqs    *QuadStore\n\ttree  *Tree\n\n\tcur *Primitive\n\n\td     quad.Direction\n\tvalue int64\n}\n\nfunc (qs *QuadStore) newIteratorContains(tree *Tree, d quad.Direction, value int64) *iteratorContains {\n\treturn &iteratorContains{\n\t\tnodes: d == 0,\n\t\tqs:    qs,\n\t\ttree:  tree,\n\t\td:     d,\n\t\tvalue: value,\n\t}\n}\n\nfunc (it *iteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorContains) Close() error {\n\treturn nil\n}\n\nfunc (it *iteratorContains) Err() error {\n\treturn nil\n}\n\nfunc (it *iteratorContains) Result() graph.Ref {\n\tif it.cur == nil {\n\t\treturn nil\n\t}\n\treturn qprim{p: it.cur}\n}\n\nfunc (it *iteratorContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tif v == nil {\n\t\treturn false\n\t}\n\tswitch v := v.(type) {\n\tcase bnode:\n\t\tif p, ok := it.tree.Get(int64(v)); ok {\n\t\t\tit.cur = p\n\t\t\treturn true\n\t\t}\n\tcase qprim:\n\t\tif v.p.Quad.Dir(it.d) == it.value {\n\t\t\tit.cur = v.p\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (it *iteratorContains) String() string {\n\treturn fmt.Sprintf(\"MemStoreContains(%v)\", it.d)\n}\n\nfunc (it *iteratorContains) Sorted() bool { return true }\n"
  },
  {
    "path": "graph/memstore/keys.go",
    "content": "// Copyright 2014 The b Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package b implements a B+tree.\n//\n// Changelog\n//\n// 2014-06-26: Lower GC presure by recycling things.\n//\n// 2014-04-18: Added new method Put.\n//\n// Generic types\n//\n// Keys and their associated values are interface{} typed, similar to all of\n// the containers in the standard library.\n//\n// Semiautomatic production of a type specific variant of this package is\n// supported via\n//\n//\t$ make generic\n//\n// This command will write to stdout a version of the btree.go file where\n// every key type occurrence is replaced by the word 'key' (written in all\n// CAPS) and every value type occurrence is replaced by the word 'value'\n// (written in all CAPS). Then you have to replace these tokens with your\n// desired type(s), using any technique you're comfortable with.\n//\n// This is how, for example, 'example/int.go' was created:\n//\n//\t$ mkdir example\n//\t$\n//\t$ # Note: the command bellow must be actually written using the words\n//\t$ # 'key' and 'value' in all CAPS. The proper form is avoided in this\n//\t$ # documentation to not confuse any text replacement mechanism.\n//\t$\n//\t$ make generic | sed -e 's/key/int/g' -e 's/value/int/g' > example/int.go\n//\n// No other changes to int.go are necessary, it compiles just fine.\n//\n// Running the benchmarks for 1000 keys on a machine with Intel i5-4670 CPU @\n// 3.4GHz, Go release 1.3.\n//\n//\t$ go test -bench 1e3 example/all_test.go example/int.go\n//\tPASS\n//\tBenchmarkSetSeq1e3\t   10000\t    146740 ns/op\n//\tBenchmarkGetSeq1e3\t   10000\t    108261 ns/op\n//\tBenchmarkSetRnd1e3\t   10000\t    254359 ns/op\n//\tBenchmarkGetRnd1e3\t   10000\t    134621 ns/op\n//\tBenchmarkDelRnd1e3\t   10000\t    211864 ns/op\n//\tBenchmarkSeekSeq1e3\t   10000\t    148628 ns/op\n//\tBenchmarkSeekRnd1e3\t   10000\t    215166 ns/op\n//\tBenchmarkNext1e3\t  200000\t      9211 ns/op\n//\tBenchmarkPrev1e3\t  200000\t      8843 ns/op\n//\tok  \tcommand-line-arguments\t25.071s\n//\t$\npackage memstore\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n)\n\nconst (\n\tkx = 32 //TODO benchmark tune this number if using custom key/value type(s).\n\tkd = 32 //TODO benchmark tune this number if using custom key/value type(s).\n)\n\nfunc init() {\n\tif kd < 1 {\n\t\tpanic(fmt.Errorf(\"kd %d: out of range\", kd))\n\t}\n\n\tif kx < 2 {\n\t\tpanic(fmt.Errorf(\"kx %d: out of range\", kx))\n\t}\n}\n\nvar (\n\tbtDPool = sync.Pool{New: func() interface{} { return &d{} }}\n\tbtEPool = btEpool{sync.Pool{New: func() interface{} { return &Enumerator{} }}}\n\tbtTPool = btTpool{sync.Pool{New: func() interface{} { return &Tree{} }}}\n\tbtXPool = sync.Pool{New: func() interface{} { return &x{} }}\n)\n\ntype btTpool struct{ sync.Pool }\n\nfunc (p *btTpool) get(cmp Cmp) *Tree {\n\tx := p.Get().(*Tree)\n\tx.cmp = cmp\n\treturn x\n}\n\ntype btEpool struct{ sync.Pool }\n\nfunc (p *btEpool) get(err error, hit bool, i int, k int64, q *d, t *Tree, ver int64) *Enumerator {\n\tx := p.Get().(*Enumerator)\n\tx.err, x.hit, x.i, x.k, x.q, x.t, x.ver = err, hit, i, k, q, t, ver\n\treturn x\n}\n\ntype (\n\t// Cmp compares a and b. Return value is:\n\t//\n\t//\t< 0 if a <  b\n\t//\t  0 if a == b\n\t//\t> 0 if a >  b\n\t//\n\tCmp func(a, b int64) int\n\n\td struct { // data page\n\t\tc int\n\t\td [2*kd + 1]de\n\t\tn *d\n\t\tp *d\n\t}\n\n\tde struct { // d element\n\t\tk int64\n\t\tv *Primitive\n\t}\n\n\t// Enumerator captures the state of enumerating a tree. It is returned\n\t// from the Seek* methods. The enumerator is aware of any mutations\n\t// made to the tree in the process of enumerating it and automatically\n\t// resumes the enumeration at the proper key, if possible.\n\t//\n\t// However, once an Enumerator returns io.EOF to signal \"no more\n\t// items\", it does no more attempt to \"resync\" on tree mutation(s).  In\n\t// other words, io.EOF from an Enumaretor is \"sticky\" (idempotent).\n\tEnumerator struct {\n\t\terr error\n\t\thit bool\n\t\ti   int\n\t\tk   int64\n\t\tq   *d\n\t\tt   *Tree\n\t\tver int64\n\t}\n\n\t// Tree is a B+tree.\n\tTree struct {\n\t\tc     int\n\t\tcmp   Cmp\n\t\tfirst *d\n\t\tlast  *d\n\t\tr     interface{}\n\t\tver   int64\n\t}\n\n\txe struct { // x element\n\t\tch interface{}\n\t\tk  int64\n\t}\n\n\tx struct { // index page\n\t\tc int\n\t\tx [2*kx + 2]xe\n\t}\n)\n\nvar ( // R/O zero values\n\tzd  d\n\tzde de\n\tze  Enumerator\n\tzk  int64\n\tzt  Tree\n\tzx  x\n\tzxe xe\n)\n\nfunc clr(q interface{}) {\n\tswitch x := q.(type) {\n\tcase *x:\n\t\tfor i := 0; i <= x.c; i++ { // Ch0 Sep0 ... Chn-1 Sepn-1 Chn\n\t\t\tclr(x.x[i].ch)\n\t\t}\n\t\t*x = zx\n\t\tbtXPool.Put(x)\n\tcase *d:\n\t\t*x = zd\n\t\tbtDPool.Put(x)\n\t}\n}\n\n// -------------------------------------------------------------------------- x\n\nfunc newX(ch0 interface{}) *x {\n\tr := btXPool.Get().(*x)\n\tr.x[0].ch = ch0\n\treturn r\n}\n\nfunc (q *x) extract(i int) {\n\tq.c--\n\tif i < q.c {\n\t\tcopy(q.x[i:], q.x[i+1:q.c+1])\n\t\tq.x[q.c].ch = q.x[q.c+1].ch\n\t\tq.x[q.c].k = zk  // GC\n\t\tq.x[q.c+1] = zxe // GC\n\t}\n}\n\nfunc (q *x) insert(i int, k int64, ch interface{}) *x {\n\tc := q.c\n\tif i < c {\n\t\tq.x[c+1].ch = q.x[c].ch\n\t\tcopy(q.x[i+2:], q.x[i+1:c])\n\t\tq.x[i+1].k = q.x[i].k\n\t}\n\tc++\n\tq.c = c\n\tq.x[i].k = k\n\tq.x[i+1].ch = ch\n\treturn q\n}\n\nfunc (q *x) siblings(i int) (l, r *d) {\n\tif i >= 0 {\n\t\tif i > 0 {\n\t\t\tl = q.x[i-1].ch.(*d)\n\t\t}\n\t\tif i < q.c {\n\t\t\tr = q.x[i+1].ch.(*d)\n\t\t}\n\t}\n\treturn\n}\n\n// -------------------------------------------------------------------------- d\n\nfunc (l *d) mvL(r *d, c int) {\n\tcopy(l.d[l.c:], r.d[:c])\n\tcopy(r.d[:], r.d[c:r.c])\n\tl.c += c\n\tr.c -= c\n}\n\nfunc (l *d) mvR(r *d, c int) {\n\tcopy(r.d[c:], r.d[:r.c])\n\tcopy(r.d[:c], l.d[l.c-c:])\n\tr.c += c\n\tl.c -= c\n}\n\n// ----------------------------------------------------------------------- Tree\n\n// TreeNew returns a newly created, empty Tree. The compare function is used\n// for key collation.\nfunc TreeNew(cmp Cmp) *Tree {\n\treturn btTPool.get(cmp)\n}\n\n// Clear removes all K/V pairs from the tree.\nfunc (t *Tree) Clear() {\n\tif t.r == nil {\n\t\treturn\n\t}\n\n\tclr(t.r)\n\tt.c, t.first, t.last, t.r = 0, nil, nil, nil\n\tt.ver++\n}\n\n// Close performs Clear and recycles t to a pool for possible later reuse. No\n// references to t should exist or such references must not be used afterwards.\nfunc (t *Tree) Close() {\n\tt.Clear()\n\t*t = zt\n\tbtTPool.Put(t)\n}\n\nfunc (t *Tree) cat(p *x, q, r *d, pi int) {\n\tt.ver++\n\tq.mvL(r, r.c)\n\tif r.n != nil {\n\t\tr.n.p = q\n\t} else {\n\t\tt.last = q\n\t}\n\tq.n = r.n\n\t*r = zd\n\tbtDPool.Put(r)\n\tif p.c > 1 {\n\t\tp.extract(pi)\n\t\tp.x[pi].ch = q\n\t} else {\n\t\tswitch x := t.r.(type) {\n\t\tcase *x:\n\t\t\t*x = zx\n\t\t\tbtXPool.Put(x)\n\t\tcase *d:\n\t\t\t*x = zd\n\t\t\tbtDPool.Put(x)\n\t\t}\n\t\tt.r = q\n\t}\n}\n\nfunc (t *Tree) catX(p, q, r *x, pi int) {\n\tt.ver++\n\tq.x[q.c].k = p.x[pi].k\n\tcopy(q.x[q.c+1:], r.x[:r.c])\n\tq.c += r.c + 1\n\tq.x[q.c].ch = r.x[r.c].ch\n\t*r = zx\n\tbtXPool.Put(r)\n\tif p.c > 1 {\n\t\tp.c--\n\t\tpc := p.c\n\t\tif pi < pc {\n\t\t\tp.x[pi].k = p.x[pi+1].k\n\t\t\tcopy(p.x[pi+1:], p.x[pi+2:pc+1])\n\t\t\tp.x[pc].ch = p.x[pc+1].ch\n\t\t\tp.x[pc].k = zk     // GC\n\t\t\tp.x[pc+1].ch = nil // GC\n\t\t}\n\t\treturn\n\t}\n\n\tswitch x := t.r.(type) {\n\tcase *x:\n\t\t*x = zx\n\t\tbtXPool.Put(x)\n\tcase *d:\n\t\t*x = zd\n\t\tbtDPool.Put(x)\n\t}\n\tt.r = q\n}\n\n// Delete removes the k's KV pair, if it exists, in which case Delete returns\n// true.\nfunc (t *Tree) Delete(k int64) (ok bool) {\n\tpi := -1\n\tvar p *x\n\tq := t.r\n\tif q == nil {\n\t\treturn false\n\t}\n\n\tfor {\n\t\tvar i int\n\t\ti, ok = t.find(q, k)\n\t\tif ok {\n\t\t\tswitch x := q.(type) {\n\t\t\tcase *x:\n\t\t\t\tif x.c < kx && q != t.r {\n\t\t\t\t\tx, i = t.underflowX(p, x, pi, i)\n\t\t\t\t}\n\t\t\t\tpi = i + 1\n\t\t\t\tp = x\n\t\t\t\tq = x.x[pi].ch\n\t\t\t\tok = false\n\t\t\t\tcontinue\n\t\t\tcase *d:\n\t\t\t\tt.extract(x, i)\n\t\t\t\tif x.c >= kd {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\tif q != t.r {\n\t\t\t\t\tt.underflow(p, x, pi)\n\t\t\t\t} else if t.c == 0 {\n\t\t\t\t\tt.Clear()\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tswitch x := q.(type) {\n\t\tcase *x:\n\t\t\tif x.c < kx && q != t.r {\n\t\t\t\tx, i = t.underflowX(p, x, pi, i)\n\t\t\t}\n\t\t\tpi = i\n\t\t\tp = x\n\t\t\tq = x.x[i].ch\n\t\tcase *d:\n\t\t\treturn false\n\t\t}\n\t}\n}\n\nfunc (t *Tree) extract(q *d, i int) { // (r *primitive) {\n\tt.ver++\n\t//r = q.d[i].v // prepared for Extract\n\tq.c--\n\tif i < q.c {\n\t\tcopy(q.d[i:], q.d[i+1:q.c+1])\n\t}\n\tq.d[q.c] = zde // GC\n\tt.c--\n\treturn\n}\n\nfunc (t *Tree) find(q interface{}, k int64) (i int, ok bool) {\n\tvar mk int64\n\tl := 0\n\tswitch x := q.(type) {\n\tcase *x:\n\t\th := x.c - 1\n\t\tfor l <= h {\n\t\t\tm := (l + h) >> 1\n\t\t\tmk = x.x[m].k\n\t\t\tswitch cmp := t.cmp(k, mk); {\n\t\t\tcase cmp > 0:\n\t\t\t\tl = m + 1\n\t\t\tcase cmp == 0:\n\t\t\t\treturn m, true\n\t\t\tdefault:\n\t\t\t\th = m - 1\n\t\t\t}\n\t\t}\n\tcase *d:\n\t\th := x.c - 1\n\t\tfor l <= h {\n\t\t\tm := (l + h) >> 1\n\t\t\tmk = x.d[m].k\n\t\t\tswitch cmp := t.cmp(k, mk); {\n\t\t\tcase cmp > 0:\n\t\t\t\tl = m + 1\n\t\t\tcase cmp == 0:\n\t\t\t\treturn m, true\n\t\t\tdefault:\n\t\t\t\th = m - 1\n\t\t\t}\n\t\t}\n\t}\n\treturn l, false\n}\n\n// First returns the first item of the tree in the key collating order, or\n// (zero-value, zero-value) if the tree is empty.\nfunc (t *Tree) First() (k int64, v *Primitive) {\n\tif q := t.first; q != nil {\n\t\tq := &q.d[0]\n\t\tk, v = q.k, q.v\n\t}\n\treturn\n}\n\n// Get returns the value associated with k and true if it exists. Otherwise Get\n// returns (zero-value, false).\nfunc (t *Tree) Get(k int64) (v *Primitive, ok bool) {\n\tq := t.r\n\tif q == nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tvar i int\n\t\tif i, ok = t.find(q, k); ok {\n\t\t\tswitch x := q.(type) {\n\t\t\tcase *x:\n\t\t\t\tq = x.x[i+1].ch\n\t\t\t\tcontinue\n\t\t\tcase *d:\n\t\t\t\treturn x.d[i].v, true\n\t\t\t}\n\t\t}\n\t\tswitch x := q.(type) {\n\t\tcase *x:\n\t\t\tq = x.x[i].ch\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (t *Tree) insert(q *d, i int, k int64, v *Primitive) *d {\n\tt.ver++\n\tc := q.c\n\tif i < c {\n\t\tcopy(q.d[i+1:], q.d[i:c])\n\t}\n\tc++\n\tq.c = c\n\tq.d[i].k, q.d[i].v = k, v\n\tt.c++\n\treturn q\n}\n\n// Last returns the last item of the tree in the key collating order, or\n// (zero-value, zero-value) if the tree is empty.\nfunc (t *Tree) Last() (k int64, v *Primitive) {\n\tif q := t.last; q != nil {\n\t\tq := &q.d[q.c-1]\n\t\tk, v = q.k, q.v\n\t}\n\treturn\n}\n\n// Len returns the number of items in the tree.\nfunc (t *Tree) Len() int {\n\treturn t.c\n}\n\nfunc (t *Tree) overflow(p *x, q *d, pi, i int, k int64, v *Primitive) {\n\tt.ver++\n\tl, r := p.siblings(pi)\n\n\tif l != nil && l.c < 2*kd {\n\t\tl.mvL(q, 1)\n\t\tt.insert(q, i-1, k, v)\n\t\tp.x[pi-1].k = q.d[0].k\n\t\treturn\n\t}\n\n\tif r != nil && r.c < 2*kd {\n\t\tif i < 2*kd {\n\t\t\tq.mvR(r, 1)\n\t\t\tt.insert(q, i, k, v)\n\t\t\tp.x[pi].k = r.d[0].k\n\t\t} else {\n\t\t\tt.insert(r, 0, k, v)\n\t\t\tp.x[pi].k = k\n\t\t}\n\t\treturn\n\t}\n\n\tt.split(p, q, pi, i, k, v)\n}\n\n// Seek returns an Enumerator positioned on a an item such that k >= item's\n// key. ok reports if k == item.key The Enumerator's position is possibly\n// after the last item in the tree.\nfunc (t *Tree) Seek(k int64) (e *Enumerator, ok bool) {\n\tq := t.r\n\tif q == nil {\n\t\te = btEPool.get(nil, false, 0, k, nil, t, t.ver)\n\t\treturn\n\t}\n\n\tfor {\n\t\tvar i int\n\t\tif i, ok = t.find(q, k); ok {\n\t\t\tswitch x := q.(type) {\n\t\t\tcase *x:\n\t\t\t\tq = x.x[i+1].ch\n\t\t\t\tcontinue\n\t\t\tcase *d:\n\t\t\t\treturn btEPool.get(nil, ok, i, k, x, t, t.ver), true\n\t\t\t}\n\t\t}\n\n\t\tswitch x := q.(type) {\n\t\tcase *x:\n\t\t\tq = x.x[i].ch\n\t\tcase *d:\n\t\t\treturn btEPool.get(nil, ok, i, k, x, t, t.ver), false\n\t\t}\n\t}\n}\n\n// SeekFirst returns an enumerator positioned on the first KV pair in the tree,\n// if any. For an empty tree, err == io.EOF is returned and e will be nil.\nfunc (t *Tree) SeekFirst() (e *Enumerator, err error) {\n\tq := t.first\n\tif q == nil {\n\t\treturn nil, io.EOF\n\t}\n\n\treturn btEPool.get(nil, true, 0, q.d[0].k, q, t, t.ver), nil\n}\n\n// SeekLast returns an enumerator positioned on the last KV pair in the tree,\n// if any. For an empty tree, err == io.EOF is returned and e will be nil.\nfunc (t *Tree) SeekLast() (e *Enumerator, err error) {\n\tq := t.last\n\tif q == nil {\n\t\treturn nil, io.EOF\n\t}\n\n\treturn btEPool.get(nil, true, q.c-1, q.d[q.c-1].k, q, t, t.ver), nil\n}\n\n// Set sets the value associated with k.\nfunc (t *Tree) Set(k int64, v *Primitive) {\n\t//dbg(\"--- PRE Set(%v, %v)\\n%s\", k, v, t.dump())\n\t//defer func() {\n\t//\tdbg(\"--- POST\\n%s\\n====\\n\", t.dump())\n\t//}()\n\n\tpi := -1\n\tvar p *x\n\tq := t.r\n\tif q == nil {\n\t\tz := t.insert(btDPool.Get().(*d), 0, k, v)\n\t\tt.r, t.first, t.last = z, z, z\n\t\treturn\n\t}\n\n\tfor {\n\t\ti, ok := t.find(q, k)\n\t\tif ok {\n\t\t\tswitch x := q.(type) {\n\t\t\tcase *x:\n\t\t\t\tif x.c > 2*kx {\n\t\t\t\t\tx, i = t.splitX(p, x, pi, i)\n\t\t\t\t}\n\t\t\t\tpi = i + 1\n\t\t\t\tp = x\n\t\t\t\tq = x.x[i+1].ch\n\t\t\t\tcontinue\n\t\t\tcase *d:\n\t\t\t\tx.d[i].v = v\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tswitch x := q.(type) {\n\t\tcase *x:\n\t\t\tif x.c > 2*kx {\n\t\t\t\tx, i = t.splitX(p, x, pi, i)\n\t\t\t}\n\t\t\tpi = i\n\t\t\tp = x\n\t\t\tq = x.x[i].ch\n\t\tcase *d:\n\t\t\tswitch {\n\t\t\tcase x.c < 2*kd:\n\t\t\t\tt.insert(x, i, k, v)\n\t\t\tdefault:\n\t\t\t\tt.overflow(p, x, pi, i, k, v)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Put combines Get and Set in a more efficient way where the tree is walked\n// only once. The upd(ater) receives (old-value, true) if a KV pair for k\n// exists or (zero-value, false) otherwise. It can then return a (new-value,\n// true) to create or overwrite the existing value in the KV pair, or\n// (whatever, false) if it decides not to create or not to update the value of\n// the KV pair.\n//\n// \ttree.Set(k, v) call conceptually equals calling\n//\n// \ttree.Put(k, func(int64, bool){ return v, true })\n//\n// modulo the differing return values.\nfunc (t *Tree) Put(k int64, upd func(oldV *Primitive, exists bool) (newV *Primitive, write bool)) (oldV *Primitive, written bool) {\n\tpi := -1\n\tvar p *x\n\tq := t.r\n\tvar newV *Primitive\n\tif q == nil {\n\t\t// new KV pair in empty tree\n\t\tnewV, written = upd(newV, false)\n\t\tif !written {\n\t\t\treturn\n\t\t}\n\n\t\tz := t.insert(btDPool.Get().(*d), 0, k, newV)\n\t\tt.r, t.first, t.last = z, z, z\n\t\treturn\n\t}\n\n\tfor {\n\t\ti, ok := t.find(q, k)\n\t\tif ok {\n\t\t\tswitch x := q.(type) {\n\t\t\tcase *x:\n\t\t\t\tif x.c > 2*kx {\n\t\t\t\t\tx, i = t.splitX(p, x, pi, i)\n\t\t\t\t}\n\t\t\t\tpi = i + 1\n\t\t\t\tp = x\n\t\t\t\tq = x.x[i+1].ch\n\t\t\t\tcontinue\n\t\t\tcase *d:\n\t\t\t\toldV = x.d[i].v\n\t\t\t\tnewV, written = upd(oldV, true)\n\t\t\t\tif !written {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tx.d[i].v = newV\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tswitch x := q.(type) {\n\t\tcase *x:\n\t\t\tif x.c > 2*kx {\n\t\t\t\tx, i = t.splitX(p, x, pi, i)\n\t\t\t}\n\t\t\tpi = i\n\t\t\tp = x\n\t\t\tq = x.x[i].ch\n\t\tcase *d: // new KV pair\n\t\t\tnewV, written = upd(newV, false)\n\t\t\tif !written {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tswitch {\n\t\t\tcase x.c < 2*kd:\n\t\t\t\tt.insert(x, i, k, newV)\n\t\t\tdefault:\n\t\t\t\tt.overflow(p, x, pi, i, k, newV)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (t *Tree) split(p *x, q *d, pi, i int, k int64, v *Primitive) {\n\tt.ver++\n\tr := btDPool.Get().(*d)\n\tif q.n != nil {\n\t\tr.n = q.n\n\t\tr.n.p = r\n\t} else {\n\t\tt.last = r\n\t}\n\tq.n = r\n\tr.p = q\n\n\tcopy(r.d[:], q.d[kd:2*kd])\n\tfor i := range q.d[kd:] {\n\t\tq.d[kd+i] = zde\n\t}\n\tq.c = kd\n\tr.c = kd\n\tvar done bool\n\tif i > kd {\n\t\tdone = true\n\t\tt.insert(r, i-kd, k, v)\n\t}\n\tif pi >= 0 {\n\t\tp.insert(pi, r.d[0].k, r)\n\t} else {\n\t\tt.r = newX(q).insert(0, r.d[0].k, r)\n\t}\n\tif done {\n\t\treturn\n\t}\n\n\tt.insert(q, i, k, v)\n}\n\nfunc (t *Tree) splitX(p *x, q *x, pi int, i int) (*x, int) {\n\tt.ver++\n\tr := btXPool.Get().(*x)\n\tcopy(r.x[:], q.x[kx+1:])\n\tq.c = kx\n\tr.c = kx\n\tif pi >= 0 {\n\t\tp.insert(pi, q.x[kx].k, r)\n\t\tq.x[kx].k = zk\n\t\tfor i := range q.x[kx+1:] {\n\t\t\tq.x[kx+i+1] = zxe\n\t\t}\n\n\t\tswitch {\n\t\tcase i < kx:\n\t\t\treturn q, i\n\t\tcase i == kx:\n\t\t\treturn p, pi\n\t\tdefault: // i > kx\n\t\t\treturn r, i - kx - 1\n\t\t}\n\t}\n\n\tnr := newX(q).insert(0, q.x[kx].k, r)\n\tt.r = nr\n\tq.x[kx].k = zk\n\tfor i := range q.x[kx+1:] {\n\t\tq.x[kx+i+1] = zxe\n\t}\n\n\tswitch {\n\tcase i < kx:\n\t\treturn q, i\n\tcase i == kx:\n\t\treturn nr, 0\n\tdefault: // i > kx\n\t\treturn r, i - kx - 1\n\t}\n}\n\nfunc (t *Tree) underflow(p *x, q *d, pi int) {\n\tt.ver++\n\tl, r := p.siblings(pi)\n\n\tif l != nil && l.c+q.c >= 2*kd {\n\t\tl.mvR(q, 1)\n\t\tp.x[pi-1].k = q.d[0].k\n\t} else if r != nil && q.c+r.c >= 2*kd {\n\t\tq.mvL(r, 1)\n\t\tp.x[pi].k = r.d[0].k\n\t\tr.d[r.c] = zde // GC\n\t} else if l != nil {\n\t\tt.cat(p, l, q, pi-1)\n\t} else {\n\t\tt.cat(p, q, r, pi)\n\t}\n}\n\nfunc (t *Tree) underflowX(p *x, q *x, pi int, i int) (*x, int) {\n\tt.ver++\n\tvar l, r *x\n\n\tif pi >= 0 {\n\t\tif pi > 0 {\n\t\t\tl = p.x[pi-1].ch.(*x)\n\t\t}\n\t\tif pi < p.c {\n\t\t\tr = p.x[pi+1].ch.(*x)\n\t\t}\n\t}\n\n\tif l != nil && l.c > kx {\n\t\tq.x[q.c+1].ch = q.x[q.c].ch\n\t\tcopy(q.x[1:], q.x[:q.c])\n\t\tq.x[0].ch = l.x[l.c].ch\n\t\tq.x[0].k = p.x[pi-1].k\n\t\tq.c++\n\t\ti++\n\t\tl.c--\n\t\tp.x[pi-1].k = l.x[l.c].k\n\t\treturn q, i\n\t}\n\n\tif r != nil && r.c > kx {\n\t\tq.x[q.c].k = p.x[pi].k\n\t\tq.c++\n\t\tq.x[q.c].ch = r.x[0].ch\n\t\tp.x[pi].k = r.x[0].k\n\t\tcopy(r.x[:], r.x[1:r.c])\n\t\tr.c--\n\t\trc := r.c\n\t\tr.x[rc].ch = r.x[rc+1].ch\n\t\tr.x[rc].k = zk\n\t\tr.x[rc+1].ch = nil\n\t\treturn q, i\n\t}\n\n\tif l != nil {\n\t\ti += l.c + 1\n\t\tt.catX(p, l, q, pi-1)\n\t\tq = l\n\t\treturn q, i\n\t}\n\n\tt.catX(p, q, r, pi)\n\treturn q, i\n}\n\n// ----------------------------------------------------------------- Enumerator\n\n// Close recycles e to a pool for possible later reuse. No references to e\n// should exist or such references must not be used afterwards.\nfunc (e *Enumerator) Close() {\n\t*e = ze\n\tbtEPool.Put(e)\n}\n\n// Next returns the currently enumerated item, if it exists and moves to the\n// next item in the key collation order. If there is no item to return, err ==\n// io.EOF is returned.\nfunc (e *Enumerator) Next() (k int64, v *Primitive, err error) {\n\tif err = e.err; err != nil {\n\t\treturn\n\t}\n\n\tif e.ver != e.t.ver {\n\t\tf, hit := e.t.Seek(e.k)\n\t\tif !e.hit && hit {\n\t\t\tif err = f.next(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t*e = *f\n\t\tf.Close()\n\t}\n\tif e.q == nil {\n\t\te.err, err = io.EOF, io.EOF\n\t\treturn\n\t}\n\n\tif e.i >= e.q.c {\n\t\tif err = e.next(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\ti := e.q.d[e.i]\n\tk, v = i.k, i.v\n\te.k, e.hit = k, false\n\te.next()\n\treturn\n}\n\nfunc (e *Enumerator) next() error {\n\tif e.q == nil {\n\t\te.err = io.EOF\n\t\treturn io.EOF\n\t}\n\n\tswitch {\n\tcase e.i < e.q.c-1:\n\t\te.i++\n\tdefault:\n\t\tif e.q, e.i = e.q.n, 0; e.q == nil {\n\t\t\te.err = io.EOF\n\t\t}\n\t}\n\treturn e.err\n}\n\n// Prev returns the currently enumerated item, if it exists and moves to the\n// previous item in the key collation order. If there is no item to return, err\n// == io.EOF is returned.\nfunc (e *Enumerator) Prev() (k int64, v *Primitive, err error) {\n\tif err = e.err; err != nil {\n\t\treturn\n\t}\n\n\tif e.ver != e.t.ver {\n\t\tf, hit := e.t.Seek(e.k)\n\t\tif !e.hit && hit {\n\t\t\tif err = f.prev(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t*e = *f\n\t\tf.Close()\n\t}\n\tif e.q == nil {\n\t\te.err, err = io.EOF, io.EOF\n\t\treturn\n\t}\n\n\tif e.i >= e.q.c {\n\t\tif err = e.next(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\ti := e.q.d[e.i]\n\tk, v = i.k, i.v\n\te.k, e.hit = k, false\n\te.prev()\n\treturn\n}\n\nfunc (e *Enumerator) prev() error {\n\tif e.q == nil {\n\t\te.err = io.EOF\n\t\treturn io.EOF\n\t}\n\n\tswitch {\n\tcase e.i > 0:\n\t\te.i--\n\tdefault:\n\t\tif e.q = e.q.p; e.q == nil {\n\t\t\te.err = io.EOF\n\t\t\tbreak\n\t\t}\n\n\t\te.i = e.q.c - 1\n\t}\n\treturn e.err\n}\n"
  },
  {
    "path": "graph/memstore/keys_test.go",
    "content": "// Copyright 2014 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage memstore\n\nimport (\n\t\"math\"\n\t\"runtime/debug\"\n\t\"testing\"\n\n\t\"github.com/cznic/mathutil\"\n)\n\nfunc rng() *mathutil.FC32 {\n\tx, err := mathutil.NewFC32(math.MinInt32/4, math.MaxInt32/4, false)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn x\n}\n\nfunc BenchmarkSetSeq1e3(b *testing.B) {\n\tbenchmarkSetSeq(b, 1e3)\n}\n\nfunc BenchmarkSetSeq1e4(b *testing.B) {\n\tbenchmarkSetSeq(b, 1e4)\n}\n\nfunc BenchmarkSetSeq1e5(b *testing.B) {\n\tbenchmarkSetSeq(b, 1e5)\n}\n\nfunc BenchmarkSetSeq1e6(b *testing.B) {\n\tbenchmarkSetSeq(b, 1e6)\n}\n\nfunc benchmarkSetSeq(b *testing.B, n int) {\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tr := TreeNew(cmp)\n\t\tdebug.FreeOSMemory()\n\t\tb.StartTimer()\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\tr.Set(j, nil)\n\t\t}\n\t\tb.StopTimer()\n\t\tr.Close()\n\t}\n\tb.StopTimer()\n}\n\nfunc BenchmarkGetSeq1e3(b *testing.B) {\n\tbenchmarkGetSeq(b, 1e3)\n}\n\nfunc BenchmarkGetSeq1e4(b *testing.B) {\n\tbenchmarkGetSeq(b, 1e4)\n}\n\nfunc BenchmarkGetSeq1e5(b *testing.B) {\n\tbenchmarkGetSeq(b, 1e5)\n}\n\nfunc BenchmarkGetSeq1e6(b *testing.B) {\n\tbenchmarkGetSeq(b, 1e6)\n}\n\nfunc benchmarkGetSeq(b *testing.B, n int) {\n\tr := TreeNew(cmp)\n\tfor i := int64(0); i < int64(n); i++ {\n\t\tr.Set(i, nil)\n\t}\n\tdebug.FreeOSMemory()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\tr.Get(j)\n\t\t}\n\t}\n\tb.StopTimer()\n\tr.Close()\n}\n\nfunc BenchmarkSetRnd1e3(b *testing.B) {\n\tbenchmarkSetRnd(b, 1e3)\n}\n\nfunc BenchmarkSetRnd1e4(b *testing.B) {\n\tbenchmarkSetRnd(b, 1e4)\n}\n\nfunc BenchmarkSetRnd1e5(b *testing.B) {\n\tbenchmarkSetRnd(b, 1e5)\n}\n\nfunc BenchmarkSetRnd1e6(b *testing.B) {\n\tbenchmarkSetRnd(b, 1e6)\n}\n\nfunc benchmarkSetRnd(b *testing.B, n int) {\n\trng := rng()\n\ta := make([]int, n)\n\tfor i := range a {\n\t\ta[i] = rng.Next()\n\t}\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tr := TreeNew(cmp)\n\t\tdebug.FreeOSMemory()\n\t\tb.StartTimer()\n\t\tfor _, v := range a {\n\t\t\tr.Set(int64(v), nil)\n\t\t}\n\t\tb.StopTimer()\n\t\tr.Close()\n\t}\n\tb.StopTimer()\n}\n\nfunc BenchmarkGetRnd1e3(b *testing.B) {\n\tbenchmarkGetRnd(b, 1e3)\n}\n\nfunc BenchmarkGetRnd1e4(b *testing.B) {\n\tbenchmarkGetRnd(b, 1e4)\n}\n\nfunc BenchmarkGetRnd1e5(b *testing.B) {\n\tbenchmarkGetRnd(b, 1e5)\n}\n\nfunc BenchmarkGetRnd1e6(b *testing.B) {\n\tbenchmarkGetRnd(b, 1e6)\n}\n\nfunc benchmarkGetRnd(b *testing.B, n int) {\n\tr := TreeNew(cmp)\n\trng := rng()\n\ta := make([]int64, n)\n\tfor i := range a {\n\t\ta[i] = int64(rng.Next())\n\t}\n\tfor _, v := range a {\n\t\tr.Set(v, nil)\n\t}\n\tdebug.FreeOSMemory()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, v := range a {\n\t\t\tr.Get(v)\n\t\t}\n\t}\n\tb.StopTimer()\n\tr.Close()\n}\n\nfunc BenchmarkDelSeq1e3(b *testing.B) {\n\tbenchmarkDelSeq(b, 1e3)\n}\n\nfunc BenchmarkDelSeq1e4(b *testing.B) {\n\tbenchmarkDelSeq(b, 1e4)\n}\n\nfunc BenchmarkDelSeq1e5(b *testing.B) {\n\tbenchmarkDelSeq(b, 1e5)\n}\n\nfunc BenchmarkDelSeq1e6(b *testing.B) {\n\tbenchmarkDelSeq(b, 1e6)\n}\n\nfunc benchmarkDelSeq(b *testing.B, n int) {\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tr := TreeNew(cmp)\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\tr.Set(j, nil)\n\t\t}\n\t\tdebug.FreeOSMemory()\n\t\tb.StartTimer()\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\tr.Delete(j)\n\t\t}\n\t}\n\tb.StopTimer()\n}\n\nfunc BenchmarkDelRnd1e3(b *testing.B) {\n\tbenchmarkDelRnd(b, 1e3)\n}\n\nfunc BenchmarkDelRnd1e4(b *testing.B) {\n\tbenchmarkDelRnd(b, 1e4)\n}\n\nfunc BenchmarkDelRnd1e5(b *testing.B) {\n\tbenchmarkDelRnd(b, 1e5)\n}\n\nfunc BenchmarkDelRnd1e6(b *testing.B) {\n\tbenchmarkDelRnd(b, 1e6)\n}\n\nfunc benchmarkDelRnd(b *testing.B, n int) {\n\trng := rng()\n\ta := make([]int64, n)\n\tfor i := range a {\n\t\ta[i] = int64(rng.Next())\n\t}\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tr := TreeNew(cmp)\n\t\tfor _, v := range a {\n\t\t\tr.Set(v, nil)\n\t\t}\n\t\tdebug.FreeOSMemory()\n\t\tb.StartTimer()\n\t\tfor _, v := range a {\n\t\t\tr.Delete(v)\n\t\t}\n\t\tb.StopTimer()\n\t\tr.Close()\n\t}\n\tb.StopTimer()\n}\n\nfunc BenchmarkSeekSeq1e3(b *testing.B) {\n\tbenchmarkSeekSeq(b, 1e3)\n}\n\nfunc BenchmarkSeekSeq1e4(b *testing.B) {\n\tbenchmarkSeekSeq(b, 1e4)\n}\n\nfunc BenchmarkSeekSeq1e5(b *testing.B) {\n\tbenchmarkSeekSeq(b, 1e5)\n}\n\nfunc BenchmarkSeekSeq1e6(b *testing.B) {\n\tbenchmarkSeekSeq(b, 1e6)\n}\n\nfunc benchmarkSeekSeq(b *testing.B, n int) {\n\tfor i := 0; i < b.N; i++ {\n\t\tb.StopTimer()\n\t\tt := TreeNew(cmp)\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\tt.Set(j, nil)\n\t\t}\n\t\tdebug.FreeOSMemory()\n\t\tb.StartTimer()\n\t\tfor j := int64(0); j < int64(n); j++ {\n\t\t\te, _ := t.Seek(j)\n\t\t\te.Close()\n\t\t}\n\t\tb.StopTimer()\n\t\tt.Close()\n\t}\n\tb.StopTimer()\n}\n\nfunc BenchmarkSeekRnd1e3(b *testing.B) {\n\tbenchmarkSeekRnd(b, 1e3)\n}\n\nfunc BenchmarkSeekRnd1e4(b *testing.B) {\n\tbenchmarkSeekRnd(b, 1e4)\n}\n\nfunc BenchmarkSeekRnd1e5(b *testing.B) {\n\tbenchmarkSeekRnd(b, 1e5)\n}\n\nfunc BenchmarkSeekRnd1e6(b *testing.B) {\n\tbenchmarkSeekRnd(b, 1e6)\n}\n\nfunc benchmarkSeekRnd(b *testing.B, n int) {\n\tr := TreeNew(cmp)\n\trng := rng()\n\ta := make([]int64, n)\n\tfor i := range a {\n\t\ta[i] = int64(rng.Next())\n\t}\n\tfor _, v := range a {\n\t\tr.Set(v, nil)\n\t}\n\tdebug.FreeOSMemory()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, v := range a {\n\t\t\te, _ := r.Seek(v)\n\t\t\te.Close()\n\t\t}\n\t}\n\tb.StopTimer()\n\tr.Close()\n}\n\nfunc BenchmarkNext1e3(b *testing.B) {\n\tbenchmarkNext(b, 1e3)\n}\n\nfunc BenchmarkNext1e4(b *testing.B) {\n\tbenchmarkNext(b, 1e4)\n}\n\nfunc BenchmarkNext1e5(b *testing.B) {\n\tbenchmarkNext(b, 1e5)\n}\n\nfunc BenchmarkNext1e6(b *testing.B) {\n\tbenchmarkNext(b, 1e6)\n}\n\nfunc benchmarkNext(b *testing.B, n int) {\n\tt := TreeNew(cmp)\n\tfor i := int64(0); i < int64(n); i++ {\n\t\tt.Set(i, nil)\n\t}\n\tdebug.FreeOSMemory()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ten, err := t.SeekFirst()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tm := 0\n\t\tfor {\n\t\t\tif _, _, err = en.Next(); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tm++\n\t\t}\n\t\tif m != n {\n\t\t\tb.Fatal(m)\n\t\t}\n\t}\n\tb.StopTimer()\n\tt.Close()\n}\n\nfunc BenchmarkPrev1e3(b *testing.B) {\n\tbenchmarkPrev(b, 1e3)\n}\n\nfunc BenchmarkPrev1e4(b *testing.B) {\n\tbenchmarkPrev(b, 1e4)\n}\n\nfunc BenchmarkPrev1e5(b *testing.B) {\n\tbenchmarkPrev(b, 1e5)\n}\n\nfunc BenchmarkPrev1e6(b *testing.B) {\n\tbenchmarkPrev(b, 1e6)\n}\n\nfunc benchmarkPrev(b *testing.B, n int) {\n\tt := TreeNew(cmp)\n\tfor i := int64(0); i < int64(n); i++ {\n\t\tt.Set(i, nil)\n\t}\n\tdebug.FreeOSMemory()\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\ten, err := t.SeekLast()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tm := 0\n\t\tfor {\n\t\t\tif _, _, err = en.Prev(); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tm++\n\t\t}\n\t\tif m != n {\n\t\t\tb.Fatal(m)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "graph/memstore/quadstore.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage memstore\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nconst QuadStoreType = \"memstore\"\n\nfunc init() {\n\tgraph.RegisterQuadStore(QuadStoreType, graph.QuadStoreRegistration{\n\t\tNewFunc: func(string, graph.Options) (graph.QuadStore, error) {\n\t\t\treturn newQuadStore(), nil\n\t\t},\n\t\tUpgradeFunc:  nil,\n\t\tInitFunc:     nil,\n\t\tIsPersistent: false,\n\t})\n}\n\ntype bnode int64\n\nfunc (n bnode) Key() interface{} { return n }\n\ntype qprim struct {\n\tp *Primitive\n}\n\nfunc (n qprim) Key() interface{} { return n.p.ID }\n\nvar _ quad.Writer = (*QuadStore)(nil)\n\nfunc cmp(a, b int64) int {\n\treturn int(a - b)\n}\n\ntype QuadDirectionIndex struct {\n\tindex [4]map[int64]*Tree\n}\n\nfunc NewQuadDirectionIndex() QuadDirectionIndex {\n\treturn QuadDirectionIndex{[...]map[int64]*Tree{\n\t\tquad.Subject - 1:   make(map[int64]*Tree),\n\t\tquad.Predicate - 1: make(map[int64]*Tree),\n\t\tquad.Object - 1:    make(map[int64]*Tree),\n\t\tquad.Label - 1:     make(map[int64]*Tree),\n\t}}\n}\n\nfunc (qdi QuadDirectionIndex) Tree(d quad.Direction, id int64) *Tree {\n\tif d < quad.Subject || d > quad.Label {\n\t\tpanic(\"illegal direction\")\n\t}\n\ttree, ok := qdi.index[d-1][id]\n\tif !ok {\n\t\ttree = TreeNew(cmp)\n\t\tqdi.index[d-1][id] = tree\n\t}\n\treturn tree\n}\n\nfunc (qdi QuadDirectionIndex) Get(d quad.Direction, id int64) (*Tree, bool) {\n\tif d < quad.Subject || d > quad.Label {\n\t\tpanic(\"illegal direction\")\n\t}\n\ttree, ok := qdi.index[d-1][id]\n\treturn tree, ok\n}\n\ntype Primitive struct {\n\tID    int64\n\tQuad  internalQuad\n\tValue quad.Value\n\trefs  int\n}\n\ntype internalQuad struct {\n\tS, P, O, L int64\n}\n\nfunc (q internalQuad) Zero() bool {\n\treturn q == (internalQuad{})\n}\n\nfunc (q *internalQuad) SetDir(d quad.Direction, n int64) {\n\tswitch d {\n\tcase quad.Subject:\n\t\tq.S = n\n\tcase quad.Predicate:\n\t\tq.P = n\n\tcase quad.Object:\n\t\tq.O = n\n\tcase quad.Label:\n\t\tq.L = n\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unknown dir: %v\", d))\n\t}\n}\nfunc (q internalQuad) Dir(d quad.Direction) int64 {\n\tvar n int64\n\tswitch d {\n\tcase quad.Subject:\n\t\tn = q.S\n\tcase quad.Predicate:\n\t\tn = q.P\n\tcase quad.Object:\n\t\tn = q.O\n\tcase quad.Label:\n\t\tn = q.L\n\t}\n\treturn n\n}\n\ntype QuadStore struct {\n\tlast int64\n\t// TODO: string -> quad.Value once Raw -> typed resolution is unnecessary\n\tvals    map[string]int64\n\tquads   map[internalQuad]int64\n\tprim    map[int64]*Primitive\n\tall     []*Primitive // might not be sorted by id\n\treading bool         // someone else might be reading \"all\" slice - next insert/delete should clone it\n\tindex   QuadDirectionIndex\n\thorizon int64 // used only to assign ids to tx\n\t// vip_index map[string]map[int64]map[string]map[int64]*b.Tree\n}\n\n// New creates a new in-memory quad store and loads provided quads.\nfunc New(quads ...quad.Quad) *QuadStore {\n\tqs := newQuadStore()\n\tfor _, q := range quads {\n\t\tqs.AddQuad(q)\n\t}\n\treturn qs\n}\n\nfunc newQuadStore() *QuadStore {\n\treturn &QuadStore{\n\t\tvals:  make(map[string]int64),\n\t\tquads: make(map[internalQuad]int64),\n\t\tprim:  make(map[int64]*Primitive),\n\t\tindex: NewQuadDirectionIndex(),\n\t}\n}\n\nfunc (qs *QuadStore) cloneAll() []*Primitive {\n\tqs.reading = true\n\treturn qs.all\n}\n\nfunc (qs *QuadStore) addPrimitive(p *Primitive) int64 {\n\tqs.last++\n\tid := qs.last\n\tp.ID = id\n\tp.refs = 1\n\tqs.appendPrimitive(p)\n\treturn id\n}\n\nfunc (qs *QuadStore) appendPrimitive(p *Primitive) {\n\tqs.prim[p.ID] = p\n\tif !qs.reading {\n\t\tqs.all = append(qs.all, p)\n\t} else {\n\t\tn := len(qs.all)\n\t\tqs.all = append(qs.all[:n:n], p) // reallocate slice\n\t\tqs.reading = false               // this is a new slice\n\t}\n}\n\nconst internalBNodePrefix = \"memnode\"\n\nfunc (qs *QuadStore) resolveVal(v quad.Value, add bool) (int64, bool) {\n\tif v == nil {\n\t\treturn 0, false\n\t}\n\tif n, ok := v.(quad.BNode); ok && strings.HasPrefix(string(n), internalBNodePrefix) {\n\t\tn = n[len(internalBNodePrefix):]\n\t\tid, err := strconv.ParseInt(string(n), 10, 64)\n\t\tif err == nil && id != 0 {\n\t\t\tif p, ok := qs.prim[id]; ok || !add {\n\t\t\t\tif add {\n\t\t\t\t\tp.refs++\n\t\t\t\t}\n\t\t\t\treturn id, ok\n\t\t\t}\n\t\t\tqs.appendPrimitive(&Primitive{ID: id, refs: 1})\n\t\t\treturn id, true\n\t\t}\n\t}\n\tvs := v.String()\n\tif id, exists := qs.vals[vs]; exists || !add {\n\t\tif exists && add {\n\t\t\tqs.prim[id].refs++\n\t\t}\n\t\treturn id, exists\n\t}\n\tid := qs.addPrimitive(&Primitive{Value: v})\n\tqs.vals[vs] = id\n\treturn id, true\n}\n\nfunc (qs *QuadStore) resolveQuad(q quad.Quad, add bool) (internalQuad, bool) {\n\tvar p internalQuad\n\tfor dir := quad.Subject; dir <= quad.Label; dir++ {\n\t\tv := q.Get(dir)\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif vid, _ := qs.resolveVal(v, add); vid != 0 {\n\t\t\tp.SetDir(dir, vid)\n\t\t} else if !add {\n\t\t\treturn internalQuad{}, false\n\t\t}\n\t}\n\treturn p, true\n}\n\nfunc (qs *QuadStore) lookupVal(id int64) quad.Value {\n\tpv := qs.prim[id]\n\tif pv == nil || pv.Value == nil {\n\t\treturn quad.BNode(internalBNodePrefix + strconv.FormatInt(id, 10))\n\t}\n\treturn pv.Value\n}\n\nfunc (qs *QuadStore) lookupQuadDirs(p internalQuad) quad.Quad {\n\tvar q quad.Quad\n\tfor dir := quad.Subject; dir <= quad.Label; dir++ {\n\t\tvid := p.Dir(dir)\n\t\tif vid == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tv := qs.lookupVal(vid)\n\t\tq.Set(dir, v)\n\t}\n\treturn q\n}\n\n// AddNode adds a blank node (with no value) to quad store. It returns an id of the node.\nfunc (qs *QuadStore) AddBNode() int64 {\n\treturn qs.addPrimitive(&Primitive{})\n}\n\n// AddNode adds a value to quad store. It returns an id of the value.\n// False is returned as a second parameter if value exists already.\nfunc (qs *QuadStore) AddValue(v quad.Value) (int64, bool) {\n\tid, exists := qs.resolveVal(v, true)\n\treturn id, !exists\n}\n\nfunc (qs *QuadStore) indexesForQuad(q internalQuad) []*Tree {\n\ttrees := make([]*Tree, 0, 4)\n\tfor dir := quad.Subject; dir <= quad.Label; dir++ {\n\t\tv := q.Dir(dir)\n\t\tif v == 0 {\n\t\t\tcontinue\n\t\t}\n\t\ttrees = append(trees, qs.index.Tree(dir, v))\n\t}\n\treturn trees\n}\n\n// AddQuad adds a quad to quad store. It returns an id of the quad.\n// False is returned as a second parameter if quad exists already.\nfunc (qs *QuadStore) AddQuad(q quad.Quad) (int64, bool) {\n\tp, _ := qs.resolveQuad(q, false)\n\tif id := qs.quads[p]; id != 0 {\n\t\treturn id, false\n\t}\n\tp, _ = qs.resolveQuad(q, true)\n\tpr := &Primitive{Quad: p}\n\tid := qs.addPrimitive(pr)\n\tqs.quads[p] = id\n\tfor _, t := range qs.indexesForQuad(p) {\n\t\tt.Set(id, pr)\n\t}\n\t// TODO(barakmich): Add VIP indexing\n\treturn id, true\n}\n\n// WriteQuad adds a quad to quad store.\n//\n// Deprecated: use AddQuad instead.\nfunc (qs *QuadStore) WriteQuad(q quad.Quad) error {\n\tqs.AddQuad(q)\n\treturn nil\n}\n\n// WriteQuads implements quad.Writer.\nfunc (qs *QuadStore) WriteQuads(buf []quad.Quad) (int, error) {\n\tfor _, q := range buf {\n\t\tqs.AddQuad(q)\n\t}\n\treturn len(buf), nil\n}\n\nfunc (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn &quadWriter{qs: qs}, nil\n}\n\ntype quadWriter struct {\n\tqs *QuadStore\n}\n\nfunc (w *quadWriter) WriteQuad(q quad.Quad) error {\n\tw.qs.AddQuad(q)\n\treturn nil\n}\n\nfunc (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\tfor _, q := range buf {\n\t\tw.qs.AddQuad(q)\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\treturn nil\n}\n\nfunc (qs *QuadStore) deleteQuadNodes(q internalQuad) {\n\tfor dir := quad.Subject; dir <= quad.Label; dir++ {\n\t\tid := q.Dir(dir)\n\t\tif id == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif p := qs.prim[id]; p != nil {\n\t\t\tp.refs--\n\t\t\tif p.refs < 0 {\n\t\t\t\tpanic(\"remove of deleted node\")\n\t\t\t} else if p.refs == 0 {\n\t\t\t\tqs.Delete(id)\n\t\t\t}\n\t\t}\n\t}\n}\nfunc (qs *QuadStore) Delete(id int64) bool {\n\tp := qs.prim[id]\n\tif p == nil {\n\t\treturn false\n\t}\n\t// remove from value index\n\tif p.Value != nil {\n\t\tdelete(qs.vals, p.Value.String())\n\t}\n\t// remove from quad indexes\n\tfor _, t := range qs.indexesForQuad(p.Quad) {\n\t\tt.Delete(id)\n\t}\n\tdelete(qs.quads, p.Quad)\n\t// remove primitive\n\tdelete(qs.prim, id)\n\tdi := -1\n\tfor i, p2 := range qs.all {\n\t\tif p == p2 {\n\t\t\tdi = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif di >= 0 {\n\t\tif !qs.reading {\n\t\t\tqs.all = append(qs.all[:di], qs.all[di+1:]...)\n\t\t} else {\n\t\t\tall := make([]*Primitive, 0, len(qs.all)-1)\n\t\t\tall = append(all, qs.all[:di]...)\n\t\t\tall = append(all, qs.all[di+1:]...)\n\t\t\tqs.all = all\n\t\t\tqs.reading = false // this is a new slice\n\t\t}\n\t}\n\tqs.deleteQuadNodes(p.Quad)\n\treturn true\n}\n\nfunc (qs *QuadStore) findQuad(q quad.Quad) (int64, internalQuad, bool) {\n\tp, ok := qs.resolveQuad(q, false)\n\tif !ok {\n\t\treturn 0, p, false\n\t}\n\tid := qs.quads[p]\n\treturn id, p, id != 0\n}\n\nfunc (qs *QuadStore) ApplyDeltas(deltas []graph.Delta, ignoreOpts graph.IgnoreOpts) error {\n\t// Precheck the whole transaction (if required)\n\tif !ignoreOpts.IgnoreDup || !ignoreOpts.IgnoreMissing {\n\t\tfor _, d := range deltas {\n\t\t\tswitch d.Action {\n\t\t\tcase graph.Add:\n\t\t\t\tif !ignoreOpts.IgnoreDup {\n\t\t\t\t\tif _, _, ok := qs.findQuad(d.Quad); ok {\n\t\t\t\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrQuadExists}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase graph.Delete:\n\t\t\t\tif !ignoreOpts.IgnoreMissing {\n\t\t\t\t\tif _, _, ok := qs.findQuad(d.Quad); !ok {\n\t\t\t\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrQuadNotExist}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrInvalidAction}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, d := range deltas {\n\t\tswitch d.Action {\n\t\tcase graph.Add:\n\t\t\tqs.AddQuad(d.Quad)\n\t\tcase graph.Delete:\n\t\t\tif id, _, ok := qs.findQuad(d.Quad); ok {\n\t\t\t\tqs.Delete(id)\n\t\t\t}\n\t\tdefault:\n\t\t\t// TODO: ideally we should rollback it\n\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrInvalidAction}\n\t\t}\n\t}\n\tqs.horizon++\n\treturn nil\n}\n\nfunc asID(v graph.Ref) (int64, bool) {\n\tswitch v := v.(type) {\n\tcase bnode:\n\t\treturn int64(v), true\n\tcase qprim:\n\t\treturn v.p.ID, true\n\tdefault:\n\t\treturn 0, false\n\t}\n}\n\nfunc (qs *QuadStore) quad(v graph.Ref) (q internalQuad, ok bool) {\n\tswitch v := v.(type) {\n\tcase bnode:\n\t\tp := qs.prim[int64(v)]\n\t\tif p == nil {\n\t\t\treturn\n\t\t}\n\t\tq = p.Quad\n\tcase qprim:\n\t\tq = v.p.Quad\n\tdefault:\n\t\treturn internalQuad{}, false\n\t}\n\treturn q, !q.Zero()\n}\n\nfunc (qs *QuadStore) Quad(index graph.Ref) (quad.Quad, error) {\n\tq, ok := qs.quad(index)\n\tif !ok {\n\t\treturn quad.Quad{}, nil\n\t}\n\treturn qs.lookupQuadDirs(q), nil\n}\n\nfunc (qs *QuadStore) QuadIterator(d quad.Direction, value graph.Ref) iterator.Shape {\n\tid, ok := asID(value)\n\tif !ok {\n\t\treturn iterator.NewNull()\n\t}\n\tindex, ok := qs.index.Get(d, id)\n\tif ok && index.Len() != 0 {\n\t\treturn qs.newIterator(index, d, id)\n\t}\n\treturn iterator.NewNull()\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, v graph.Ref) (refs.Size, error) {\n\tid, ok := asID(v)\n\tif !ok {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t}\n\tindex, ok := qs.index.Get(d, id)\n\tif !ok {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t}\n\treturn refs.Size{Value: int64(index.Len()), Exact: true}, nil\n}\n\nfunc (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\treturn graph.Stats{\n\t\tNodes: refs.Size{\n\t\t\tValue: int64(len(qs.vals)),\n\t\t\tExact: true,\n\t\t},\n\t\tQuads: refs.Size{\n\t\t\tValue: int64(len(qs.quads)),\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\nfunc (qs *QuadStore) ValueOf(name quad.Value) (graph.Ref, error) {\n\tif name == nil {\n\t\treturn nil, nil\n\t}\n\tid := qs.vals[name.String()]\n\tif id == 0 {\n\t\treturn nil, nil\n\t}\n\treturn bnode(id), nil\n}\n\nfunc (qs *QuadStore) NameOf(v graph.Ref) (quad.Value, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t} else if v, ok := v.(refs.PreFetchedValue); ok {\n\t\treturn v.NameOf(), nil\n\t}\n\tn, ok := asID(v)\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\tif _, ok = qs.prim[n]; !ok {\n\t\treturn nil, nil\n\t}\n\treturn qs.lookupVal(n), nil\n}\n\nfunc (qs *QuadStore) QuadsAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(false, qs.last)\n}\n\nfunc (qs *QuadStore) QuadDirection(val graph.Ref, d quad.Direction) (graph.Ref, error) {\n\tq, ok := qs.quad(val)\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\tid := q.Dir(d)\n\tif id == 0 {\n\t\treturn nil, nil\n\t}\n\treturn bnode(id), nil\n}\n\nfunc (qs *QuadStore) NodesAllIterator() iterator.Shape {\n\treturn qs.newAllIterator(true, qs.last)\n}\n\nfunc (qs *QuadStore) Close() error { return nil }\n"
  },
  {
    "path": "graph/memstore/quadstore_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage memstore\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/cayley/writer\"\n)\n\n// This is a simple test graph.\n//\n//\t+---+                        +---+\n//\t| A |-------               ->| F |<--\n//\t+---+       \\------>+---+-/  +---+   \\--+---+\n//\t             ------>|#B#|      |        | E |\n//\t+---+-------/      >+---+      |        +---+\n//\t| C |             /            v\n//\t+---+           -/           +---+\n//\t  ----    +---+/             |#G#|\n//\t      \\-->|#D#|------------->+---+\n//\t          +---+\nvar simpleGraph = []quad.Quad{\n\tquad.MakeRaw(\"A\", \"follows\", \"B\", \"\"),\n\tquad.MakeRaw(\"C\", \"follows\", \"B\", \"\"),\n\tquad.MakeRaw(\"C\", \"follows\", \"D\", \"\"),\n\tquad.MakeRaw(\"D\", \"follows\", \"B\", \"\"),\n\tquad.MakeRaw(\"B\", \"follows\", \"F\", \"\"),\n\tquad.MakeRaw(\"F\", \"follows\", \"G\", \"\"),\n\tquad.MakeRaw(\"D\", \"follows\", \"G\", \"\"),\n\tquad.MakeRaw(\"E\", \"follows\", \"F\", \"\"),\n\tquad.MakeRaw(\"B\", \"status\", \"cool\", \"status_graph\"),\n\tquad.MakeRaw(\"D\", \"status\", \"cool\", \"status_graph\"),\n\tquad.MakeRaw(\"G\", \"status\", \"cool\", \"status_graph\"),\n}\n\nfunc makeTestStore(data []quad.Quad) (*QuadStore, graph.QuadWriter, []pair) {\n\tseen := make(map[string]struct{})\n\tqs := New()\n\tvar (\n\t\tval int64\n\t\tind []pair\n\t)\n\twriter, _ := writer.NewSingleReplication(qs, nil)\n\tfor _, t := range data {\n\t\tfor _, dir := range quad.Directions {\n\t\t\tqp := t.GetString(dir)\n\t\t\tif _, ok := seen[qp]; !ok && qp != \"\" {\n\t\t\t\tval++\n\t\t\t\tind = append(ind, pair{qp, val})\n\t\t\t\tseen[qp] = struct{}{}\n\t\t\t}\n\t\t}\n\n\t\twriter.AddQuad(t)\n\t\tval++\n\t}\n\treturn qs, writer, ind\n}\n\nfunc TestMemstore(t *testing.T) {\n\tgraphtest.TestAll(t, func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\treturn New(), nil\n\t}, &graphtest.Config{\n\t\tAlwaysRunIntegration: true,\n\t})\n}\n\nfunc BenchmarkMemstore(b *testing.B) {\n\tgraphtest.BenchmarkAll(b, func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\treturn New(), nil\n\t}, &graphtest.Config{\n\t\tAlwaysRunIntegration: true,\n\t})\n}\n\ntype pair struct {\n\tquery string\n\tvalue int64\n}\n\nfunc TestMemstoreValueOf(t *testing.T) {\n\tqs, _, index := makeTestStore(simpleGraph)\n\texp := graph.Stats{\n\t\tNodes: refs.Size{Value: 11, Exact: true},\n\t\tQuads: refs.Size{Value: 11, Exact: true},\n\t}\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, exp, st, \"Unexpected quadstore size\")\n\n\tfor _, test := range index {\n\t\tv, err := qs.ValueOf(quad.Raw(test.query))\n\t\trequire.NoError(t, err)\n\t\tswitch v := v.(type) {\n\t\tdefault:\n\t\t\tt.Errorf(\"ValueOf(%q) returned unexpected type, got:%T expected int64\", test.query, v)\n\t\tcase bnode:\n\t\t\trequire.Equal(t, test.value, int64(v))\n\t\t}\n\t}\n}\n\nfunc TestIteratorsAndNextResultOrderA(t *testing.T) {\n\tctx := context.TODO()\n\tqs, _, _ := makeTestStore(simpleGraph)\n\n\tfixed := iterator.NewFixed()\n\tqsv, err := qs.ValueOf(quad.Raw(\"C\"))\n\trequire.NoError(t, err)\n\tfixed.Add(qsv)\n\n\tfixed2 := iterator.NewFixed()\n\tqsv, err = qs.ValueOf(quad.Raw(\"follows\"))\n\trequire.NoError(t, err)\n\tfixed2.Add(qsv)\n\n\tall := qs.NodesAllIterator()\n\n\tconst allTag = \"all\"\n\tinnerAnd := iterator.NewAnd(\n\t\tgraph.NewLinksTo(qs, fixed2, quad.Predicate),\n\t\tgraph.NewLinksTo(qs, iterator.Tag(all, allTag), quad.Object),\n\t)\n\n\thasa := graph.NewHasA(qs, innerAnd, quad.Subject)\n\touterAnd := iterator.NewAnd(fixed, hasa).Iterate()\n\n\tif !outerAnd.Next(ctx) {\n\t\tt.Error(\"Expected one matching subtree\")\n\t}\n\tval := outerAnd.Result()\n\tvn, err := qs.NameOf(val)\n\trequire.NoError(t, err)\n\tif vn != quad.Raw(\"C\") {\n\t\tt.Errorf(\"Matching subtree should be %s, got %s\", \"barak\", vn)\n\t}\n\n\tvar (\n\t\tgot    []string\n\t\texpect = []string{\"B\", \"D\"}\n\t)\n\tfor {\n\t\tm := make(map[string]graph.Ref, 1)\n\t\touterAnd.TagResults(m)\n\t\tmv, err := qs.NameOf(m[allTag])\n\t\trequire.NoError(t, err)\n\t\tgot = append(got, quad.ToString(mv))\n\t\tif !outerAnd.NextPath(ctx) {\n\t\t\tbreak\n\t\t}\n\t}\n\tsort.Strings(got)\n\n\tif !reflect.DeepEqual(got, expect) {\n\t\tt.Errorf(\"Unexpected result, got:%q expect:%q\", got, expect)\n\t}\n\n\tif outerAnd.Next(ctx) {\n\t\tt.Error(\"More than one possible top level output?\")\n\t}\n}\n\nfunc TestLinksToOptimization(t *testing.T) {\n\tqs, _, _ := makeTestStore(simpleGraph)\n\n\tlto := shape.BuildIterator(context.TODO(), qs, shape.Quads{\n\t\t{Dir: quad.Object, Values: shape.Lookup{quad.Raw(\"cool\")}},\n\t})\n\n\tnewIt, changed := lto.Optimize(context.TODO())\n\tif changed {\n\t\tt.Errorf(\"unexpected optimization step\")\n\t}\n\tif _, ok := newIt.(*Iterator); !ok {\n\t\tt.Fatal(\"Didn't swap out to LLRB\")\n\t}\n}\n\nfunc TestRemoveQuad(t *testing.T) {\n\tctx := context.TODO()\n\tqs, w, _ := makeTestStore(simpleGraph)\n\n\terr := w.RemoveQuad(quad.Make(\n\t\t\"E\",\n\t\t\"follows\",\n\t\t\"F\",\n\t\tnil,\n\t))\n\n\tif err != nil {\n\t\tt.Error(\"Couldn't remove quad\", err)\n\t}\n\n\tfixed := iterator.NewFixed()\n\tqsv, err := qs.ValueOf(quad.Raw(\"E\"))\n\trequire.NoError(t, err)\n\tfixed.Add(qsv)\n\n\tfixed2 := iterator.NewFixed()\n\tqsv, err = qs.ValueOf(quad.Raw(\"follows\"))\n\trequire.NoError(t, err)\n\tfixed2.Add(qsv)\n\n\tinnerAnd := iterator.NewAnd(\n\t\tgraph.NewLinksTo(qs, fixed, quad.Subject),\n\t\tgraph.NewLinksTo(qs, fixed2, quad.Predicate),\n\t)\n\n\thasa := graph.NewHasA(qs, innerAnd, quad.Object)\n\n\tnewIt, _ := hasa.Optimize(ctx)\n\tif newIt.Iterate().Next(ctx) {\n\t\tt.Error(\"E should not have any followers.\")\n\t}\n}\n\nfunc TestTransaction(t *testing.T) {\n\tqs, w, _ := makeTestStore(simpleGraph)\n\tst, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\n\ttx := graph.NewTransaction()\n\ttx.AddQuad(quad.Make(\n\t\t\"E\",\n\t\t\"follows\",\n\t\t\"G\",\n\t\tnil))\n\ttx.RemoveQuad(quad.Make(\n\t\t\"Non\",\n\t\t\"existent\",\n\t\t\"quad\",\n\t\tnil))\n\n\terr = w.ApplyTransaction(tx)\n\tif err == nil {\n\t\tt.Error(\"Able to remove a non-existent quad\")\n\t}\n\tst2, err := qs.Stats(context.Background(), true)\n\trequire.NoError(t, err)\n\trequire.Equal(t, st, st2, \"Appended a new quad in a failed transaction\")\n}\n"
  },
  {
    "path": "graph/nosql/all/all.go",
    "content": "package all\n\nimport (\n\t_ \"github.com/hidal-go/hidalgo/legacy/nosql/all\"\n\n\t_ \"github.com/cayleygraph/cayley/graph/nosql\"\n)\n"
  },
  {
    "path": "graph/nosql/all/all_test.go",
    "content": "// +build docker\n\npackage all\n\nimport (\n\t\"testing\"\n\n\t_ \"github.com/hidal-go/hidalgo/legacy/nosql/nosqltest/all\"\n\n\t\"github.com/cayleygraph/cayley/graph/nosql/nosqltest\"\n\thnosqltest \"github.com/hidal-go/hidalgo/legacy/nosql/nosqltest\"\n)\n\nfunc TestNoSQL(t *testing.T) {\n\thnosqltest.RunTest(t, nosqltest.TestAll)\n}\n\nfunc BenchmarkNoSQL(t *testing.B) {\n\thnosqltest.RunBenchmark(t, nosqltest.BenchmarkAll)\n}\n"
  },
  {
    "path": "graph/nosql/elastic/elastic.go",
    "content": "package elastic\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\t\"github.com/hidal-go/hidalgo/legacy/nosql/elastic\"\n\n\t//import hidal-go first so the registration of the no sql stores occurs before quadstore iterates for registration\n\tgnosql \"github.com/cayleygraph/cayley/graph/nosql\"\n)\n\nconst Type = elastic.Name\n\nfunc Create(addr string, opt graph.Options) (nosql.Database, error) {\n\treturn elastic.Dial(context.TODO(), addr, gnosql.DefaultDBName, nosql.Options(opt))\n}\n\nfunc Open(addr string, opt graph.Options) (nosql.Database, error) {\n\treturn elastic.Dial(context.TODO(), addr, gnosql.DefaultDBName, nosql.Options(opt))\n}\n"
  },
  {
    "path": "graph/nosql/iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage nosql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\ntype Linkage struct {\n\tDir quad.Direction\n\tVal NodeHash\n}\n\nfunc linkageToFilters(links []Linkage) []nosql.FieldFilter {\n\tfilters := make([]nosql.FieldFilter, 0, len(links))\n\tfor _, l := range links {\n\t\tfilters = append(filters, nosql.FieldFilter{\n\t\t\tPath:   []string{l.Dir.String()},\n\t\t\tFilter: nosql.Equal,\n\t\t\tValue:  nosql.String(l.Val),\n\t\t})\n\t}\n\treturn filters\n}\n\ntype Iterator struct {\n\tqs         *QuadStore\n\tcollection string\n\tlimit      int64\n\tconstraint []nosql.FieldFilter\n\tlinks      []Linkage // used in Contains\n\n\tsize refs.Size\n\terr  error\n}\n\nfunc (qs *QuadStore) newLinksToIterator(collection string, links []Linkage) *Iterator {\n\tfilters := linkageToFilters(links)\n\tit := qs.newIterator(collection, filters...)\n\tit.links = links\n\treturn it\n}\n\nfunc (qs *QuadStore) newIterator(collection string, constraints ...nosql.FieldFilter) *Iterator {\n\treturn &Iterator{\n\t\tqs:         qs,\n\t\tconstraint: constraints,\n\t\tcollection: collection,\n\t\tsize:       refs.Size{Value: -1},\n\t}\n}\n\nfunc (it *Iterator) Iterate() iterator.Scanner {\n\treturn it.qs.newIteratorNext(it.collection, it.constraint, it.limit)\n}\n\nfunc (it *Iterator) Lookup() iterator.Index {\n\treturn it.qs.newIteratorContains(it.collection, it.constraint, it.links, it.limit)\n}\n\nfunc (it *Iterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *Iterator) getSize(ctx context.Context) (refs.Size, error) {\n\tif it.size.Value == -1 {\n\t\tsize, err := it.qs.getSize(it.collection, it.constraint)\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t}\n\t\tit.size = refs.Size{\n\t\t\tValue: size,\n\t\t\tExact: true,\n\t\t}\n\t}\n\tif it.limit > 0 && it.size.Value > it.limit {\n\t\tit.size.Value = it.limit\n\t}\n\tif it.size.Value < 0 {\n\t\treturn refs.Size{\n\t\t\tValue: it.qs.Size(),\n\t\t\tExact: false,\n\t\t}, it.err\n\t}\n\treturn it.size, nil\n}\n\nfunc (it *Iterator) Sorted() bool                                        { return true }\nfunc (it *Iterator) Optimize(ctx context.Context) (iterator.Shape, bool) { return it, false }\n\nfunc (it *Iterator) String() string {\n\treturn fmt.Sprintf(\"NoSQL(%v)\", it.collection)\n}\n\nfunc (it *Iterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\tsize, err := it.getSize(ctx)\n\treturn iterator.Costs{\n\t\tContainsCost: 1,\n\t\tNextCost:     5,\n\t\tSize:         size,\n\t}, err\n}\n\ntype iteratorNext struct {\n\tqs         *QuadStore\n\tcollection string\n\tlimit      int64\n\tconstraint []nosql.FieldFilter\n\n\titer   nosql.DocIterator\n\tresult graph.Ref\n\terr    error\n}\n\nfunc (qs *QuadStore) newIteratorNext(collection string, constraints []nosql.FieldFilter, limit int64) *iteratorNext {\n\treturn &iteratorNext{\n\t\tqs:         qs,\n\t\tconstraint: constraints,\n\t\tcollection: collection,\n\t\tlimit:      limit,\n\t}\n}\n\nfunc (it *iteratorNext) makeIterator(ctx context.Context) nosql.DocIterator {\n\tq := it.qs.db.Query(it.collection)\n\tif len(it.constraint) != 0 {\n\t\tq = q.WithFields(it.constraint...)\n\t}\n\tif it.limit > 0 {\n\t\tq = q.Limit(int(it.limit))\n\t}\n\treturn q.Iterate(ctx)\n}\n\nfunc (it *iteratorNext) Close() error {\n\tif it.iter != nil {\n\t\treturn it.iter.Close()\n\t}\n\treturn nil\n}\n\nfunc (it *iteratorNext) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorNext) Next(ctx context.Context) bool {\n\tif it.iter == nil {\n\t\tit.iter = it.makeIterator(ctx)\n\t}\n\tvar doc nosql.Document\n\tfor {\n\t\tif !it.iter.Next(ctx) {\n\t\t\tif err := it.iter.Err(); err != nil {\n\t\t\t\tit.err = err\n\t\t\t\tclog.Errorf(\"error nexting iterator: %v\", err)\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t\tdoc = it.iter.Doc()\n\t\tif it.collection == colQuads && !checkQuadValid(doc) {\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\tif it.collection == colQuads {\n\t\tsh, _ := doc[fldSubject].(nosql.String)\n\t\tph, _ := doc[fldPredicate].(nosql.String)\n\t\toh, _ := doc[fldObject].(nosql.String)\n\t\tlh, _ := doc[fldLabel].(nosql.String)\n\t\tit.result = QuadHash{\n\t\t\tstring(sh), string(ph), string(oh), string(lh),\n\t\t}\n\t} else {\n\t\tid, _ := doc[fldHash].(nosql.String)\n\t\tit.result = NodeHash(id)\n\t}\n\treturn true\n}\n\nfunc (it *iteratorNext) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorNext) Result() graph.Ref {\n\treturn it.result\n}\n\nfunc (it *iteratorNext) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorNext) Sorted() bool { return true }\n\nfunc (it *iteratorNext) String() string {\n\treturn fmt.Sprintf(\"NoSQLNext(%v)\", it.collection)\n}\n\ntype iteratorContains struct {\n\tqs         *QuadStore\n\tcollection string\n\tlimit      int64 // FIXME(dennwc): doesn't work right now\n\tconstraint []nosql.FieldFilter\n\tlinks      []Linkage\n\n\titer   nosql.DocIterator\n\tresult graph.Ref\n\terr    error\n}\n\nfunc (qs *QuadStore) newIteratorContains(collection string, constraints []nosql.FieldFilter, links []Linkage, limit int64) *iteratorContains {\n\treturn &iteratorContains{\n\t\tqs:         qs,\n\t\tcollection: collection,\n\t\tconstraint: constraints,\n\t\tlinks:      links,\n\t\tlimit:      limit,\n\t}\n}\n\nfunc (it *iteratorContains) makeIterator(ctx context.Context) nosql.DocIterator {\n\tq := it.qs.db.Query(it.collection)\n\tif len(it.constraint) != 0 {\n\t\tq = q.WithFields(it.constraint...)\n\t}\n\tif it.limit > 0 {\n\t\tq = q.Limit(int(it.limit))\n\t}\n\treturn q.Iterate(ctx)\n}\n\nfunc (it *iteratorContains) Close() error {\n\tif it.iter != nil {\n\t\treturn it.iter.Close()\n\t}\n\treturn nil\n}\n\nfunc (it *iteratorContains) TagResults(dst map[string]graph.Ref) {}\n\nfunc (it *iteratorContains) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorContains) Result() graph.Ref {\n\treturn it.result\n}\n\nfunc (it *iteratorContains) NextPath(ctx context.Context) bool {\n\treturn false\n}\n\nfunc (it *iteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tif len(it.links) != 0 {\n\t\tqh := v.(QuadHash)\n\t\tfor _, l := range it.links {\n\t\t\tif l.Val != NodeHash(qh.Get(l.Dir)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tit.result = v\n\t\treturn true\n\t}\n\tif len(it.constraint) == 0 {\n\t\tit.result = v\n\t\treturn true\n\t}\n\tqv, err := it.qs.NameOf(v)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tif qv == nil {\n\t\treturn false\n\t}\n\td := toDocumentValue(&it.qs.opt, qv)\n\tfor _, f := range it.constraint {\n\t\tif !f.Matches(d) {\n\t\t\treturn false\n\t\t}\n\t}\n\tit.result = v\n\treturn true\n}\n\nfunc (it *iteratorContains) Sorted() bool { return true }\n\nfunc (it *iteratorContains) String() string {\n\treturn fmt.Sprintf(\"NoSQLContains(%v)\", it.collection)\n}\n"
  },
  {
    "path": "graph/nosql/mongo/mongo.go",
    "content": "package mongo\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\t\"github.com/hidal-go/hidalgo/legacy/nosql/mongo\"\n\n\t//import hidal-go first so the registration of the no sql stores occurs before quadstore iterates for registration\n\tgnosql \"github.com/cayleygraph/cayley/graph/nosql\"\n)\n\nconst Type = mongo.Name\n\nfunc Create(addr string, opt graph.Options) (nosql.Database, error) {\n\treturn mongo.Dial(context.TODO(), addr, gnosql.DefaultDBName, nosql.Options(opt))\n}\n\nfunc Open(addr string, opt graph.Options) (nosql.Database, error) {\n\treturn mongo.Dial(context.TODO(), addr, gnosql.DefaultDBName, nosql.Options(opt))\n}\n"
  },
  {
    "path": "graph/nosql/nosqltest/nosqltest.go",
    "content": "package nosqltest\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\t\"github.com/hidal-go/hidalgo/legacy/nosql/nosqltest\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest\"\n\tgnosql \"github.com/cayleygraph/cayley/graph/nosql\"\n)\n\nfunc toConfig(c nosql.Traits) graphtest.Config {\n\treturn graphtest.Config{\n\t\tNoPrimitives:             true,\n\t\tTimeInMs:                 c.TimeInMs,\n\t\tOptimizesComparison:      true,\n\t\tSkipDeletedFromIterator:  true,\n\t\tSkipSizeCheckAfterDelete: true,\n\t}\n}\n\nfunc NewQuadStore(t testing.TB, gen nosqltest.Database) (graph.QuadStore, graph.Options) {\n\tdb := gen.Run(t)\n\terr := gnosql.Init(db, nil)\n\tif err != nil {\n\t\tdb.Close()\n\t\trequire.Fail(t, \"init failed\", \"%v\", err)\n\t}\n\ttr := gen.Traits\n\tkdb, err := gnosql.NewQuadStore(db, &tr, nil)\n\tif err != nil {\n\t\tdb.Close()\n\t\trequire.Fail(t, \"create failed\", \"%v\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tkdb.Close()\n\t})\n\treturn kdb, nil\n}\n\nfunc TestAll(t *testing.T, gen nosqltest.Database) {\n\tc := toConfig(gen.Traits)\n\tgraphtest.TestAll(t, func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\treturn NewQuadStore(t, gen)\n\t}, &c)\n}\n\nfunc BenchmarkAll(t *testing.B, gen nosqltest.Database) {\n\tc := toConfig(gen.Traits)\n\tgraphtest.BenchmarkAll(t, func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\treturn NewQuadStore(t, gen)\n\t}, &c)\n}\n"
  },
  {
    "path": "graph/nosql/ouch/ouch.go",
    "content": "package ouch\n\nimport (\n\t_ \"github.com/hidal-go/hidalgo/legacy/nosql/couch\"\n)\n"
  },
  {
    "path": "graph/nosql/quadstore.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage nosql\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/internal/lru\"\n)\n\nconst DefaultDBName = \"cayley\"\n\ntype Registration struct {\n\tNewFunc      NewFunc\n\tInitFunc     InitFunc\n\tIsPersistent bool\n\tTraits\n}\n\ntype Traits = nosql.Traits\n\nfunc init() {\n\tfor _, reg := range nosql.List() {\n\t\tRegister(reg.Name, Registration{\n\t\t\tNewFunc: func(addr string, options graph.Options) (nosql.Database, error) {\n\t\t\t\treturn reg.Open(context.TODO(), addr, DefaultDBName, nosql.Options(options))\n\t\t\t},\n\t\t\tInitFunc: func(addr string, options graph.Options) (nosql.Database, error) {\n\t\t\t\treturn reg.New(context.TODO(), addr, DefaultDBName, nosql.Options(options))\n\t\t\t},\n\t\t\tIsPersistent: !reg.Volatile, Traits: reg.Traits,\n\t\t})\n\t}\n}\n\ntype InitFunc func(string, graph.Options) (nosql.Database, error)\ntype NewFunc func(string, graph.Options) (nosql.Database, error)\n\nfunc Register(name string, r Registration) {\n\tgraph.RegisterQuadStore(name, graph.QuadStoreRegistration{\n\t\tInitFunc: func(addr string, opt graph.Options) error {\n\t\t\tif !r.IsPersistent {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdb, err := r.InitFunc(addr, opt)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer db.Close()\n\t\t\tif err = Init(db, opt); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn db.Close()\n\t\t},\n\t\tNewFunc: func(addr string, opt graph.Options) (graph.QuadStore, error) {\n\t\t\tdb, err := r.NewFunc(addr, opt)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif !r.IsPersistent {\n\t\t\t\tif err = Init(db, opt); err != nil {\n\t\t\t\t\tdb.Close()\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tnopt := r.Traits\n\t\t\tqs, err := NewQuadStore(db, &nopt, opt)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn qs, nil\n\t\t},\n\t\tIsPersistent: r.IsPersistent,\n\t})\n}\n\nfunc Init(db nosql.Database, opt graph.Options) error {\n\treturn ensureIndexes(context.TODO(), db)\n}\n\nfunc NewQuadStore(db nosql.Database, nopt *Traits, opt graph.Options) (*QuadStore, error) {\n\tif err := ensureIndexes(context.TODO(), db); err != nil {\n\t\treturn nil, err\n\t}\n\tqs := &QuadStore{\n\t\tdb:    db,\n\t\tids:   lru.New(1 << 16),\n\t\tsizes: lru.New(1 << 16),\n\t}\n\tif nopt != nil {\n\t\tqs.opt = *nopt\n\t}\n\treturn qs, nil\n}\n\ntype NodeHash string\n\nfunc (NodeHash) IsNode() bool       { return false }\nfunc (v NodeHash) Key() interface{} { return v }\nfunc (v NodeHash) key() nosql.Key   { return nosql.Key{string(v)} }\n\ntype QuadHash [4]string\n\nfunc (QuadHash) IsNode() bool       { return false }\nfunc (v QuadHash) Key() interface{} { return v }\n\nfunc (v QuadHash) Get(d quad.Direction) string {\n\tvar ind int\n\tswitch d {\n\tcase quad.Subject:\n\t\tind = 0\n\tcase quad.Predicate:\n\t\tind = 1\n\tcase quad.Object:\n\t\tind = 2\n\tcase quad.Label:\n\t\tind = 3\n\t}\n\treturn v[ind]\n}\n\nconst (\n\tcolLog   = \"log\"\n\tcolNodes = \"nodes\"\n\tcolQuads = \"quads\"\n\n\tfldLogID = \"id\"\n\n\tfldSubject     = \"subject\"\n\tfldPredicate   = \"predicate\"\n\tfldObject      = \"object\"\n\tfldLabel       = \"label\"\n\tfldQuadAdded   = \"added\"\n\tfldQuadDeleted = \"deleted\"\n\n\tfldHash  = \"hash\"\n\tfldValue = \"value\"\n\tfldSize  = \"refs\"\n\n\tfldValData   = \"str\"\n\tfldIRI       = \"iri\"\n\tfldBNode     = \"bnode\"\n\tfldType      = \"type\"\n\tfldLang      = \"lang\"\n\tfldValInt    = \"int\"\n\tfldValStrInt = \"int_str\"\n\tfldValFloat  = \"float\"\n\tfldValBool   = \"bool\"\n\tfldValTime   = \"ts\"\n\tfldValPb     = \"pb\"\n)\n\ntype QuadStore struct {\n\tdb    nosql.Database\n\tids   *lru.Cache\n\tsizes *lru.Cache\n\topt   Traits\n}\n\nfunc ensureIndexes(ctx context.Context, db nosql.Database) error {\n\terr := db.EnsureIndex(ctx, colLog, nosql.Index{\n\t\tFields: []string{fldLogID},\n\t\tType:   nosql.StringExact,\n\t}, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = db.EnsureIndex(ctx, colNodes, nosql.Index{\n\t\tFields: []string{fldHash},\n\t\tType:   nosql.StringExact,\n\t}, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = db.EnsureIndex(ctx, colQuads, nosql.Index{\n\t\tFields: []string{\n\t\t\tfldSubject,\n\t\t\tfldPredicate,\n\t\t\tfldObject,\n\t\t\tfldLabel,\n\t\t},\n\t\tType: nosql.StringExact,\n\t}, []nosql.Index{\n\t\t{Fields: []string{fldSubject}, Type: nosql.StringExact},\n\t\t{Fields: []string{fldPredicate}, Type: nosql.StringExact},\n\t\t{Fields: []string{fldObject}, Type: nosql.StringExact},\n\t\t{Fields: []string{fldLabel}, Type: nosql.StringExact},\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getKeyForQuad(t quad.Quad) nosql.Key {\n\treturn nosql.Key{\n\t\thashOf(t.Subject),\n\t\thashOf(t.Predicate),\n\t\thashOf(t.Object),\n\t\thashOf(t.Label),\n\t}\n}\n\nfunc hashOf(s quad.Value) string {\n\tif s == nil {\n\t\treturn \"\"\n\t}\n\th := quad.HashOf(s)\n\treturn base64.StdEncoding.EncodeToString(h)\n}\n\nfunc (qs *QuadStore) nameToKey(name quad.Value) nosql.Key {\n\tnode := qs.hashOf(name)\n\treturn node.key()\n}\n\nfunc (qs *QuadStore) updateNodeBy(ctx context.Context, key nosql.Key, name quad.Value, inc int) error {\n\tif inc == 0 {\n\t\treturn nil\n\t}\n\td := toDocumentValue(&qs.opt, name)\n\terr := qs.db.Update(colNodes, key).Upsert(d).Inc(fldSize, inc).Do(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error updating node: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) cleanupNodes(ctx context.Context, keys []nosql.Key) error {\n\terr := qs.db.Delete(colNodes).Keys(keys...).WithFields(nosql.FieldFilter{\n\t\tPath:   []string{fldSize},\n\t\tFilter: nosql.Equal,\n\t\tValue:  nosql.Int(0),\n\t}).Do(ctx)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error cleaning up nodes: %v\", err)\n\t}\n\treturn err\n}\n\nfunc (qs *QuadStore) updateQuad(ctx context.Context, q quad.Quad, proc graph.Procedure) error {\n\tvar setname string\n\tif proc == graph.Add {\n\t\tsetname = fldQuadAdded\n\t} else if proc == graph.Delete {\n\t\tsetname = fldQuadDeleted\n\t}\n\tdoc := nosql.Document{\n\t\tfldSubject:   nosql.String(hashOf(q.Subject)),\n\t\tfldPredicate: nosql.String(hashOf(q.Predicate)),\n\t\tfldObject:    nosql.String(hashOf(q.Object)),\n\t}\n\tif l := hashOf(q.Label); l != \"\" {\n\t\tdoc[fldLabel] = nosql.String(l)\n\t}\n\terr := qs.db.Update(colQuads, getKeyForQuad(q)).Upsert(doc).\n\t\tInc(setname, 1).Do(ctx)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"quad update failed: %v\", err)\n\t}\n\treturn err\n}\n\nfunc checkQuadValid(q nosql.Document) bool {\n\tadded, _ := asInt(q[fldQuadAdded])\n\tdeleted, _ := asInt(q[fldQuadDeleted])\n\treturn added > deleted\n}\n\nfunc (qs *QuadStore) checkValidQuad(ctx context.Context, key nosql.Key) (bool, error) {\n\tq, err := qs.db.FindByKey(ctx, colQuads, key)\n\tif err == nosql.ErrNotFound {\n\t\treturn false, nil\n\t}\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error checking quad validity: %v\", err)\n\t\treturn false, err\n\t}\n\treturn checkQuadValid(q), nil\n}\n\nfunc (qs *QuadStore) batchInsert(col string) nosql.DocWriter {\n\treturn nosql.BatchInsert(qs.db, col)\n}\n\nfunc (qs *QuadStore) appendLog(ctx context.Context, deltas []graph.Delta) ([]nosql.Key, error) {\n\tw := qs.batchInsert(colLog)\n\tdefer w.Close()\n\tfor _, d := range deltas {\n\t\tdata, err := proto.Marshal(pquads.MakeQuad(d.Quad))\n\t\tif err != nil {\n\t\t\treturn w.Keys(), err\n\t\t}\n\t\tvar action string\n\t\tif d.Action == graph.Add {\n\t\t\taction = \"AddQuadPQ\"\n\t\t} else {\n\t\t\taction = \"DeleteQuadPQ\"\n\t\t}\n\t\terr = w.WriteDoc(ctx, nil, nosql.Document{\n\t\t\t\"op\":   nosql.String(action),\n\t\t\t\"data\": nosql.Bytes(data),\n\t\t\t\"ts\":   nosql.Time(time.Now().UTC()),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn w.Keys(), err\n\t\t}\n\t}\n\terr := w.Flush(ctx)\n\treturn w.Keys(), err\n}\n\nfunc (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn &quadWriter{qs: qs}, nil\n}\n\ntype quadWriter struct {\n\tqs     *QuadStore\n\tdeltas []graph.Delta\n}\n\nfunc (w *quadWriter) WriteQuad(q quad.Quad) error {\n\t_, err := w.WriteQuads([]quad.Quad{q})\n\treturn err\n}\n\nfunc (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\t// TODO(dennwc): write an optimized implementation\n\tw.deltas = w.deltas[:0]\n\tif cap(w.deltas) < len(buf) {\n\t\tw.deltas = make([]graph.Delta, 0, len(buf))\n\t}\n\tfor _, q := range buf {\n\t\tw.deltas = append(w.deltas, graph.Delta{\n\t\t\tQuad: q, Action: graph.Add,\n\t\t})\n\t}\n\terr := w.qs.ApplyDeltas(w.deltas, graph.IgnoreOpts{\n\t\tIgnoreDup: true,\n\t})\n\tw.deltas = w.deltas[:0]\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\tw.deltas = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) ApplyDeltas(deltas []graph.Delta, ignoreOpts graph.IgnoreOpts) error {\n\tctx := context.TODO()\n\tids := make(map[quad.Value]int)\n\n\tvar validDeltas []graph.Delta\n\tif ignoreOpts.IgnoreDup || ignoreOpts.IgnoreMissing {\n\t\tvalidDeltas = make([]graph.Delta, 0, len(deltas))\n\t}\n\t// Pre-check the existence condition.\n\tfor _, d := range deltas {\n\t\tif d.Action != graph.Add && d.Action != graph.Delete {\n\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrInvalidAction}\n\t\t}\n\t\tvalid, err := qs.checkValidQuad(ctx, getKeyForQuad(d.Quad))\n\t\tif err != nil {\n\t\t\treturn &graph.DeltaError{Delta: d, Err: err}\n\t\t}\n\t\tswitch d.Action {\n\t\tcase graph.Add:\n\t\t\tif valid {\n\t\t\t\tif ignoreOpts.IgnoreDup {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrQuadExists}\n\t\t\t\t}\n\t\t\t}\n\t\tcase graph.Delete:\n\t\t\tif !valid {\n\t\t\t\tif ignoreOpts.IgnoreMissing {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\treturn &graph.DeltaError{Delta: d, Err: graph.ErrQuadNotExist}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif validDeltas != nil {\n\t\t\tvalidDeltas = append(validDeltas, d)\n\t\t}\n\t\tvar dn int\n\t\tif d.Action == graph.Add {\n\t\t\tdn = 1\n\t\t} else {\n\t\t\tdn = -1\n\t\t}\n\t\tids[d.Quad.Subject] += dn\n\t\tids[d.Quad.Object] += dn\n\t\tids[d.Quad.Predicate] += dn\n\t\tif d.Quad.Label != nil {\n\t\t\tids[d.Quad.Label] += dn\n\t\t}\n\t}\n\tif validDeltas != nil {\n\t\tdeltas = validDeltas\n\t}\n\tif oids, err := qs.appendLog(ctx, deltas); err != nil {\n\t\tif i := len(oids); i < len(deltas) {\n\t\t\treturn &graph.DeltaError{Delta: deltas[i], Err: err}\n\t\t}\n\t\treturn &graph.DeltaError{Err: err}\n\t}\n\t// make sure to create all nodes before writing any quads\n\t// concurrent reads may observe broken quads in other case\n\tvar gc []nosql.Key\n\tfor name, dn := range ids {\n\t\tkey := qs.nameToKey(name)\n\t\terr := qs.updateNodeBy(ctx, key, name, dn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif dn < 0 {\n\t\t\tgc = append(gc, key)\n\t\t}\n\t}\n\t// gc nodes that has negative ref counter\n\tif err := qs.cleanupNodes(ctx, gc); err != nil {\n\t\treturn err\n\t}\n\tfor _, d := range deltas {\n\t\terr := qs.updateQuad(ctx, d.Quad, d.Action)\n\t\tif err != nil {\n\t\t\treturn &graph.DeltaError{Delta: d, Err: err}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc toDocumentValue(opt *Traits, v quad.Value) nosql.Document {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tvar doc nosql.Document\n\tencPb := func() {\n\t\tqv := pquads.MakeValue(v)\n\t\tdata, err := proto.Marshal(qv)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdoc[fldValPb] = nosql.Bytes(data)\n\t}\n\tswitch d := v.(type) {\n\tcase quad.String:\n\t\tdoc = nosql.Document{fldValData: nosql.String(d)}\n\tcase quad.IRI:\n\t\tdoc = nosql.Document{fldValData: nosql.String(d), fldIRI: nosql.Bool(true)}\n\tcase quad.BNode:\n\t\tdoc = nosql.Document{fldValData: nosql.String(d), fldBNode: nosql.Bool(true)}\n\tcase quad.TypedString:\n\t\tdoc = nosql.Document{fldValData: nosql.String(d.Value), fldType: nosql.String(d.Type)}\n\tcase quad.LangString:\n\t\tdoc = nosql.Document{fldValData: nosql.String(d.Value), fldLang: nosql.String(d.Lang)}\n\tcase quad.Int:\n\t\tdoc = nosql.Document{fldValInt: nosql.Int(d)}\n\t\tif opt.Number32 {\n\t\t\t// store sortable string representation for range queries\n\t\t\tdoc[fldValStrInt] = nosql.String(itos(int64(d)))\n\t\t\tencPb()\n\t\t}\n\tcase quad.Float:\n\t\tdoc = nosql.Document{fldValFloat: nosql.Float(d)}\n\t\tif opt.Number32 {\n\t\t\tencPb()\n\t\t}\n\tcase quad.Bool:\n\t\tdoc = nosql.Document{fldValBool: nosql.Bool(d)}\n\tcase quad.Time:\n\t\tdoc = nosql.Document{fldValTime: nosql.Time(time.Time(d).UTC())}\n\tdefault:\n\t\tencPb()\n\t}\n\treturn nosql.Document{fldValue: doc}\n}\n\nfunc asInt(v nosql.Value) (nosql.Int, error) {\n\tvar vi nosql.Int\n\tswitch v := v.(type) {\n\tcase nosql.Int:\n\t\tvi = v\n\tcase nosql.Float:\n\t\tvi = nosql.Int(v)\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unexpected type for int field: %T\", v)\n\t}\n\treturn vi, nil\n}\n\nfunc toQuadValue(opt *Traits, d nosql.Document) (quad.Value, error) {\n\tif len(d) == 0 {\n\t\treturn nil, nil\n\t}\n\tvar err error\n\t// prefer protobuf representation\n\tif v, ok := d[fldValPb]; ok {\n\t\tvar b []byte\n\t\tswitch v := v.(type) {\n\t\tcase nosql.String:\n\t\t\tb, err = base64.StdEncoding.DecodeString(string(v))\n\t\tcase nosql.Bytes:\n\t\t\tb = []byte(v)\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"unexpected type for pb field: %T\", v)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar p pquads.Value\n\t\tif err := proto.Unmarshal(b, &p); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"couldn't decode value: %v\", err)\n\t\t}\n\t\treturn p.ToNative(), nil\n\t} else if v, ok := d[fldValInt]; ok {\n\t\tif opt.Number32 {\n\t\t\t// parse from string, so we are confident that we will get exactly the same value\n\t\t\tif vs, ok := d[fldValStrInt].(nosql.String); ok {\n\t\t\t\tiv := quad.Int(stoi(string(vs)))\n\t\t\t\treturn iv, nil\n\t\t\t}\n\t\t}\n\t\tvi, err := asInt(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn quad.Int(vi), nil\n\t} else if v, ok := d[fldValFloat]; ok {\n\t\tvar vf quad.Float\n\t\tswitch v := v.(type) {\n\t\tcase nosql.Int:\n\t\t\tvf = quad.Float(v)\n\t\tcase nosql.Float:\n\t\t\tvf = quad.Float(v)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unexpected type for float field: %T\", v)\n\t\t}\n\t\treturn vf, nil\n\t} else if v, ok := d[fldValBool]; ok {\n\t\tvar vb quad.Bool\n\t\tswitch v := v.(type) {\n\t\tcase nosql.Bool:\n\t\t\tvb = quad.Bool(v)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unexpected type for bool field: %T\", v)\n\t\t}\n\t\treturn vb, nil\n\t} else if v, ok := d[fldValTime]; ok {\n\t\tvar vt quad.Time\n\t\tswitch v := v.(type) {\n\t\tcase nosql.Time:\n\t\t\tvt = quad.Time(v)\n\t\tcase nosql.String:\n\t\t\tvar t time.Time\n\t\t\tif err := t.UnmarshalJSON([]byte(`\"` + string(v) + `\"`)); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tvt = quad.Time(t)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unexpected type for bool field: %T\", v)\n\t\t}\n\t\treturn vt, nil\n\t}\n\tvs, ok := d[fldValData].(nosql.String)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown value format: %T\", d[fldValData])\n\t}\n\tif len(d) == 1 {\n\t\treturn quad.String(vs), nil\n\t}\n\tif ok, _ := d[fldIRI].(nosql.Bool); ok {\n\t\treturn quad.IRI(vs), nil\n\t} else if ok, _ := d[fldBNode].(nosql.Bool); ok {\n\t\treturn quad.BNode(vs), nil\n\t} else if typ, ok := d[fldType].(nosql.String); ok {\n\t\treturn quad.TypedString{Value: quad.String(vs), Type: quad.IRI(typ)}, nil\n\t} else if typ, ok := d[fldLang].(nosql.String); ok {\n\t\treturn quad.LangString{Value: quad.String(vs), Lang: string(typ)}, nil\n\t}\n\treturn nil, fmt.Errorf(\"unsupported value: %#v\", d)\n}\n\nfunc (qs *QuadStore) Quad(val graph.Ref) (quad.Quad, error) {\n\th := val.(QuadHash)\n\tvar q quad.Quad\n\tvar err error\n\tq.Subject, err = qs.NameOf(NodeHash(h.Get(quad.Subject)))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Predicate, err = qs.NameOf(NodeHash(h.Get(quad.Predicate)))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Object, err = qs.NameOf(NodeHash(h.Get(quad.Object)))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Label, err = qs.NameOf(NodeHash(h.Get(quad.Label)))\n\treturn q, err\n}\n\nfunc (qs *QuadStore) QuadIterator(d quad.Direction, val graph.Ref) iterator.Shape {\n\th, ok := val.(NodeHash)\n\tif !ok {\n\t\treturn iterator.NewNull()\n\t}\n\treturn qs.newLinksToIterator(\"quads\", []Linkage{{Dir: d, Val: h}})\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, v graph.Ref) (refs.Size, error) {\n\th, ok := v.(NodeHash)\n\tif !ok {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t}\n\tsz, err := qs.getSize(\"quads\", linkageToFilters([]Linkage{{Dir: d, Val: h}}))\n\tif err != nil {\n\t\treturn refs.Size{}, err\n\t}\n\treturn refs.Size{\n\t\tValue: sz,\n\t\tExact: true,\n\t}, nil\n}\n\nfunc (qs *QuadStore) NodesAllIterator() iterator.Shape {\n\treturn qs.newIterator(\"nodes\")\n}\n\nfunc (qs *QuadStore) QuadsAllIterator() iterator.Shape {\n\treturn qs.newIterator(\"quads\")\n}\n\nfunc (qs *QuadStore) hashOf(s quad.Value) NodeHash {\n\treturn NodeHash(hashOf(s))\n}\n\nfunc (qs *QuadStore) ValueOf(s quad.Value) (graph.Ref, error) {\n\tif s == nil {\n\t\treturn nil, nil\n\t}\n\treturn qs.hashOf(s), nil\n}\n\nfunc (qs *QuadStore) NameOf(v graph.Ref) (quad.Value, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t} else if v, ok := v.(refs.PreFetchedValue); ok {\n\t\treturn v.NameOf(), nil\n\t}\n\thash := v.(NodeHash)\n\tif hash == \"\" {\n\t\treturn nil, nil\n\t}\n\tif val, ok := qs.ids.Get(string(hash)); ok {\n\t\treturn val.(quad.Value), nil\n\t}\n\tnd, err := qs.db.FindByKey(context.TODO(), colNodes, hash.key())\n\tif err == nosql.ErrNotFound {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\tclog.Errorf(\"couldn't retrieve node %v: %v\", v, err)\n\t\treturn nil, err\n\t}\n\tdv, _ := nd[fldValue].(nosql.Document)\n\tqv, err := toQuadValue(&qs.opt, dv)\n\tif err != nil {\n\t\tclog.Errorf(\"couldn't convert node %v: %v\", v, err)\n\t\treturn nil, err\n\t}\n\tif id, _ := nd[fldHash].(nosql.String); id == nosql.String(hash) && qv != nil {\n\t\tqs.ids.Put(string(hash), qv)\n\t}\n\treturn qv, nil\n}\n\nfunc (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\t// TODO(barakmich): Make size real; store it in the log, and retrieve it.\n\tnodes, err := qs.db.Query(colNodes).Count(ctx)\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\tquads, err := qs.db.Query(colQuads).Count(ctx)\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\treturn graph.Stats{\n\t\tNodes: refs.Size{\n\t\t\tValue: nodes,\n\t\t\tExact: true,\n\t\t},\n\t\tQuads: refs.Size{\n\t\t\tValue: quads,\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\nfunc (qs *QuadStore) Size() int64 {\n\tcount, err := qs.db.Query(colQuads).Count(context.TODO())\n\tif err != nil {\n\t\tclog.Errorf(\"%v\", err)\n\t\treturn 0\n\t}\n\treturn count\n}\n\nfunc (qs *QuadStore) Close() error {\n\treturn qs.db.Close()\n}\n\nfunc (qs *QuadStore) QuadDirection(in graph.Ref, d quad.Direction) (graph.Ref, error) {\n\treturn NodeHash(in.(QuadHash).Get(d)), nil\n}\n\nfunc (qs *QuadStore) getSize(col string, constraints []nosql.FieldFilter) (int64, error) {\n\tcacheKey := \"\"\n\tfor _, c := range constraints { // FIXME\n\t\tcacheKey += fmt.Sprint(c.Path, c.Filter, c.Value)\n\t}\n\tkey := col + cacheKey\n\tif val, ok := qs.sizes.Get(key); ok {\n\t\treturn val.(int64), nil\n\t}\n\tq := qs.db.Query(col)\n\tif len(constraints) != 0 {\n\t\tq = q.WithFields(constraints...)\n\t}\n\tsize, err := q.Count(context.TODO())\n\tif err != nil {\n\t\tclog.Errorf(\"error getting size for iterator: %v\", err)\n\t\treturn -1, err\n\t}\n\tqs.sizes.Put(key, int64(size))\n\treturn int64(size), nil\n}\n"
  },
  {
    "path": "graph/nosql/shapes.go",
    "content": "package nosql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/hidal-go/hidalgo/legacy/nosql\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar _ shape.Optimizer = (*QuadStore)(nil)\n\nfunc (qs *QuadStore) OptimizeShape(ctx context.Context, s shape.Shape) (shape.Shape, bool) {\n\tswitch s := s.(type) {\n\tcase shape.Quads:\n\t\treturn qs.optimizeQuads(s)\n\tcase shape.Filter:\n\t\treturn qs.optimizeFilter(s)\n\tcase shape.Page:\n\t\treturn qs.optimizePage(s)\n\tcase shape.Composite:\n\t\tif s2, opt := s.Simplify().Optimize(ctx, qs); opt {\n\t\t\treturn s2, true\n\t\t}\n\t}\n\treturn s, false\n}\n\n// Shape is a shape representing a documents query with filters\ntype Shape struct {\n\tCollection string              // name of the collection\n\tFilters    []nosql.FieldFilter // filters to select documents\n\tLimit      int64               // limits a number of documents\n}\n\nfunc (s Shape) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tdb, ok := qs.(*QuadStore)\n\tif !ok {\n\t\treturn iterator.NewError(fmt.Errorf(\"not a nosql database: %T\", qs))\n\t}\n\treturn db.newIterator(s.Collection, s.Filters...)\n}\n\nfunc (s Shape) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\treturn s, false\n}\n\n// Quads is a shape representing a quads query\ntype Quads struct {\n\tLinks []Linkage // filters to select quads\n\tLimit int64     // limits a number of documents\n}\n\nfunc (s Quads) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tdb, ok := qs.(*QuadStore)\n\tif !ok {\n\t\treturn iterator.NewError(fmt.Errorf(\"not a nosql database: %T\", qs))\n\t}\n\treturn db.newLinksToIterator(colQuads, s.Links)\n}\n\nfunc (s Quads) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\treturn s, false\n}\n\nconst int64Adjust = 1 << 63\n\n// itos serializes int64 into a sortable string 13 chars long.\nfunc itos(i int64) string {\n\ts := strconv.FormatUint(uint64(i)+int64Adjust, 32)\n\tconst z = \"0000000000000\"\n\treturn z[len(s):] + s\n}\n\n// stoi de-serializes int64 from a sortable string 13 chars long.\nfunc stoi(s string) int64 {\n\tret, err := strconv.ParseUint(s, 32, 64)\n\tif err != nil {\n\t\t//TODO handle error?\n\t\treturn 0\n\t}\n\treturn int64(ret - int64Adjust)\n}\n\nfunc toFieldFilter(opt *Traits, c shape.Comparison) ([]nosql.FieldFilter, bool) {\n\tvar op nosql.FilterOp\n\tswitch c.Op {\n\tcase iterator.CompareGT:\n\t\top = nosql.GT\n\tcase iterator.CompareGTE:\n\t\top = nosql.GTE\n\tcase iterator.CompareLT:\n\t\top = nosql.LT\n\tcase iterator.CompareLTE:\n\t\top = nosql.LTE\n\tdefault:\n\t\treturn nil, false\n\t}\n\tfieldPath := func(s string) []string {\n\t\treturn []string{fldValue, s}\n\t}\n\n\tvar filters []nosql.FieldFilter\n\tswitch v := c.Val.(type) {\n\tcase quad.String:\n\t\tfilters = []nosql.FieldFilter{\n\t\t\t{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},\n\t\t\t{Path: fieldPath(fldIRI), Filter: nosql.NotEqual, Value: nosql.Bool(true)},\n\t\t\t{Path: fieldPath(fldBNode), Filter: nosql.NotEqual, Value: nosql.Bool(true)},\n\t\t}\n\tcase quad.IRI:\n\t\tfilters = []nosql.FieldFilter{\n\t\t\t{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},\n\t\t\t{Path: fieldPath(fldIRI), Filter: nosql.Equal, Value: nosql.Bool(true)},\n\t\t}\n\tcase quad.BNode:\n\t\tfilters = []nosql.FieldFilter{\n\t\t\t{Path: fieldPath(fldValData), Filter: op, Value: nosql.String(v)},\n\t\t\t{Path: fieldPath(fldBNode), Filter: nosql.Equal, Value: nosql.Bool(true)},\n\t\t}\n\tcase quad.Int:\n\t\tif opt.Number32 && (v < math.MinInt32 || v > math.MaxInt32) {\n\t\t\t// switch to range on string values\n\t\t\tfilters = []nosql.FieldFilter{\n\t\t\t\t{Path: fieldPath(fldValStrInt), Filter: op, Value: nosql.String(itos(int64(v)))},\n\t\t\t}\n\t\t} else {\n\t\t\tfilters = []nosql.FieldFilter{\n\t\t\t\t{Path: fieldPath(fldValInt), Filter: op, Value: nosql.Int(v)},\n\t\t\t}\n\t\t}\n\tcase quad.Float:\n\t\tfilters = []nosql.FieldFilter{\n\t\t\t{Path: fieldPath(fldValFloat), Filter: op, Value: nosql.Float(v)},\n\t\t}\n\tcase quad.Time:\n\t\tfilters = []nosql.FieldFilter{\n\t\t\t{Path: fieldPath(fldValTime), Filter: op, Value: nosql.Time(v)},\n\t\t}\n\tdefault:\n\t\treturn nil, false\n\t}\n\treturn filters, true\n}\n\nfunc (qs *QuadStore) optimizeFilter(s shape.Filter) (shape.Shape, bool) {\n\tif _, ok := s.From.(shape.AllNodes); !ok {\n\t\treturn s, false\n\t}\n\tvar (\n\t\tfilters []nosql.FieldFilter\n\t\tleft    []shape.ValueFilter\n\t)\n\tfieldPath := func(s string) []string {\n\t\treturn []string{fldValue, s}\n\t}\n\tfor _, f := range s.Filters {\n\t\tswitch f := f.(type) {\n\t\tcase shape.Comparison:\n\t\t\tif fld, ok := toFieldFilter(&qs.opt, f); ok {\n\t\t\t\tfilters = append(filters, fld...)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase shape.Wildcard:\n\t\t\tfilters = append(filters, []nosql.FieldFilter{\n\t\t\t\t{Path: fieldPath(fldValData), Filter: nosql.Regexp, Value: nosql.String(f.Regexp())},\n\t\t\t}...)\n\t\t\tcontinue\n\t\tcase shape.Regexp:\n\t\t\tfilters = append(filters, []nosql.FieldFilter{\n\t\t\t\t{Path: fieldPath(fldValData), Filter: nosql.Regexp, Value: nosql.String(f.Re.String())},\n\t\t\t}...)\n\t\t\tif !f.Refs {\n\t\t\t\tfilters = append(filters, []nosql.FieldFilter{\n\t\t\t\t\t{Path: fieldPath(fldIRI), Filter: nosql.NotEqual, Value: nosql.Bool(true)},\n\t\t\t\t\t{Path: fieldPath(fldBNode), Filter: nosql.NotEqual, Value: nosql.Bool(true)},\n\t\t\t\t}...)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tleft = append(left, f)\n\t}\n\tif len(filters) == 0 {\n\t\treturn s, false\n\t}\n\tvar ns shape.Shape = Shape{Collection: colNodes, Filters: filters}\n\tif len(left) != 0 {\n\t\tns = shape.Filter{From: ns, Filters: left}\n\t}\n\treturn ns, true\n}\n\nfunc (qs *QuadStore) optimizeQuads(s shape.Quads) (shape.Shape, bool) {\n\tvar (\n\t\tlinks []Linkage\n\t\tleft  []shape.QuadFilter\n\t)\n\tfor _, f := range s {\n\t\tif v, ok := shape.One(f.Values); ok {\n\t\t\tif h, ok := v.(NodeHash); ok {\n\t\t\t\tlinks = append(links, Linkage{Dir: f.Dir, Val: h})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tleft = append(left, f)\n\t}\n\tif len(links) == 0 {\n\t\treturn s, false\n\t}\n\tvar ns shape.Shape = Quads{Links: links}\n\tif len(left) != 0 {\n\t\tns = shape.Intersect{ns, shape.Quads(left)}\n\t}\n\treturn s, true\n}\n\nfunc (qs *QuadStore) optimizePage(s shape.Page) (shape.Shape, bool) {\n\tif s.Skip != 0 {\n\t\treturn s, false\n\t}\n\tswitch f := s.From.(type) {\n\tcase shape.AllNodes:\n\t\treturn Shape{Collection: colNodes, Limit: s.Limit}, false\n\tcase Shape:\n\t\ts.ApplyPage(shape.Page{Limit: f.Limit})\n\t\tf.Limit = s.Limit\n\t\treturn f, true\n\tcase Quads:\n\t\ts.ApplyPage(shape.Page{Limit: f.Limit})\n\t\tf.Limit = s.Limit\n\t\treturn f, true\n\t}\n\treturn s, false\n}\n"
  },
  {
    "path": "graph/nosql/value_test.go",
    "content": "package nosql\n\nimport (\n\t\"math\"\n\t\"sort\"\n\t\"testing\"\n)\n\nfunc TestIntStr(t *testing.T) {\n\tvar testS []string\n\ttestI := []int64{\n\t\t120000, -4, 88, 0, -7000000, 88,\n\t\tmath.MaxInt64 - 1, math.MaxInt64,\n\t\tmath.MinInt64, math.MinInt64 + 1,\n\t}\n\tfor _, v := range testI {\n\t\ttestS = append(testS, itos(v))\n\t}\n\tsort.Strings(testS)\n\tsort.Slice(testI, func(i, j int) bool { return testI[i] < testI[j] })\n\tfor k, v := range testS {\n\t\tr := stoi(v)\n\t\tif r != testI[k] {\n\t\t\tt.Errorf(\"Sorting of stringed int64s wrong: %v %v %v\", k, r, testI[k])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "graph/proto/primitive.pb.go",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        v4.23.4\n// source: primitive.proto\n\npackage proto\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype PrimitiveType int32\n\nconst (\n\tPrimitiveType_LINK      PrimitiveType = 0\n\tPrimitiveType_IRI       PrimitiveType = 1\n\tPrimitiveType_STRING    PrimitiveType = 2\n\tPrimitiveType_BNODE     PrimitiveType = 3\n\tPrimitiveType_TYPED_STR PrimitiveType = 4\n\tPrimitiveType_LANG_STR  PrimitiveType = 5\n\tPrimitiveType_INT       PrimitiveType = 6\n\tPrimitiveType_FLOAT     PrimitiveType = 7\n\tPrimitiveType_BOOL      PrimitiveType = 8\n\tPrimitiveType_TIMESTAMP PrimitiveType = 9\n)\n\n// Enum value maps for PrimitiveType.\nvar (\n\tPrimitiveType_name = map[int32]string{\n\t\t0: \"LINK\",\n\t\t1: \"IRI\",\n\t\t2: \"STRING\",\n\t\t3: \"BNODE\",\n\t\t4: \"TYPED_STR\",\n\t\t5: \"LANG_STR\",\n\t\t6: \"INT\",\n\t\t7: \"FLOAT\",\n\t\t8: \"BOOL\",\n\t\t9: \"TIMESTAMP\",\n\t}\n\tPrimitiveType_value = map[string]int32{\n\t\t\"LINK\":      0,\n\t\t\"IRI\":       1,\n\t\t\"STRING\":    2,\n\t\t\"BNODE\":     3,\n\t\t\"TYPED_STR\": 4,\n\t\t\"LANG_STR\":  5,\n\t\t\"INT\":       6,\n\t\t\"FLOAT\":     7,\n\t\t\"BOOL\":      8,\n\t\t\"TIMESTAMP\": 9,\n\t}\n)\n\nfunc (x PrimitiveType) Enum() *PrimitiveType {\n\tp := new(PrimitiveType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PrimitiveType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PrimitiveType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_primitive_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PrimitiveType) Type() protoreflect.EnumType {\n\treturn &file_primitive_proto_enumTypes[0]\n}\n\nfunc (x PrimitiveType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PrimitiveType.Descriptor instead.\nfunc (PrimitiveType) EnumDescriptor() ([]byte, []int) {\n\treturn file_primitive_proto_rawDescGZIP(), []int{0}\n}\n\ntype Primitive struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tID        uint64 `protobuf:\"varint,1,opt,name=ID,proto3\" json:\"ID,omitempty\"`\n\tSubject   uint64 `protobuf:\"varint,2,opt,name=Subject,proto3\" json:\"Subject,omitempty\"`\n\tPredicate uint64 `protobuf:\"varint,3,opt,name=Predicate,proto3\" json:\"Predicate,omitempty\"`\n\tObject    uint64 `protobuf:\"varint,4,opt,name=Object,proto3\" json:\"Object,omitempty\"`\n\tLabel     uint64 `protobuf:\"varint,5,opt,name=Label,proto3\" json:\"Label,omitempty\"`\n\tReplaces  uint64 `protobuf:\"varint,6,opt,name=Replaces,proto3\" json:\"Replaces,omitempty\"`\n\tTimestamp int64  `protobuf:\"varint,7,opt,name=Timestamp,proto3\" json:\"Timestamp,omitempty\"`\n\tValue     []byte `protobuf:\"bytes,8,opt,name=Value,proto3\" json:\"Value,omitempty\"`\n\tDeleted   bool   `protobuf:\"varint,9,opt,name=Deleted,proto3\" json:\"Deleted,omitempty\"`\n}\n\nfunc (x *Primitive) Reset() {\n\t*x = Primitive{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_primitive_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Primitive) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Primitive) ProtoMessage() {}\n\nfunc (x *Primitive) ProtoReflect() protoreflect.Message {\n\tmi := &file_primitive_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Primitive.ProtoReflect.Descriptor instead.\nfunc (*Primitive) Descriptor() ([]byte, []int) {\n\treturn file_primitive_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Primitive) GetID() uint64 {\n\tif x != nil {\n\t\treturn x.ID\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetSubject() uint64 {\n\tif x != nil {\n\t\treturn x.Subject\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetPredicate() uint64 {\n\tif x != nil {\n\t\treturn x.Predicate\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetObject() uint64 {\n\tif x != nil {\n\t\treturn x.Object\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetLabel() uint64 {\n\tif x != nil {\n\t\treturn x.Label\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetReplaces() uint64 {\n\tif x != nil {\n\t\treturn x.Replaces\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetTimestamp() int64 {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn 0\n}\n\nfunc (x *Primitive) GetValue() []byte {\n\tif x != nil {\n\t\treturn x.Value\n\t}\n\treturn nil\n}\n\nfunc (x *Primitive) GetDeleted() bool {\n\tif x != nil {\n\t\treturn x.Deleted\n\t}\n\treturn false\n}\n\nvar File_primitive_proto protoreflect.FileDescriptor\n\nvar file_primitive_proto_rawDesc = []byte{\n\t0x0a, 0x0f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xeb, 0x01, 0x0a, 0x09, 0x50, 0x72, 0x69,\n\t0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x04, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63,\n\t0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,\n\t0x12, 0x1c, 0x0a, 0x09, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x04, 0x52, 0x09, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x16,\n\t0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06,\n\t0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x18,\n\t0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08,\n\t0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08,\n\t0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65,\n\t0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x54, 0x69, 0x6d,\n\t0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18,\n\t0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07,\n\t0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x44,\n\t0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2a, 0x83, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x6d, 0x69,\n\t0x74, 0x69, 0x76, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4e, 0x4b,\n\t0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x52, 0x49, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53,\n\t0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x4e, 0x4f, 0x44, 0x45,\n\t0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x53, 0x54, 0x52, 0x10,\n\t0x04, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x41, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x10, 0x05, 0x12,\n\t0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x4c, 0x4f, 0x41,\n\t0x54, 0x10, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x08, 0x12, 0x0d, 0x0a,\n\t0x09, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x10, 0x09, 0x42, 0x2b, 0x5a, 0x29,\n\t0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x79, 0x6c, 0x65,\n\t0x79, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x63, 0x61, 0x79, 0x6c, 0x65, 0x79, 0x2f, 0x67, 0x72,\n\t0x61, 0x70, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x33,\n}\n\nvar (\n\tfile_primitive_proto_rawDescOnce sync.Once\n\tfile_primitive_proto_rawDescData = file_primitive_proto_rawDesc\n)\n\nfunc file_primitive_proto_rawDescGZIP() []byte {\n\tfile_primitive_proto_rawDescOnce.Do(func() {\n\t\tfile_primitive_proto_rawDescData = protoimpl.X.CompressGZIP(file_primitive_proto_rawDescData)\n\t})\n\treturn file_primitive_proto_rawDescData\n}\n\nvar file_primitive_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_primitive_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_primitive_proto_goTypes = []any{\n\t(PrimitiveType)(0), // 0: proto.PrimitiveType\n\t(*Primitive)(nil),  // 1: proto.Primitive\n}\nvar file_primitive_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_primitive_proto_init() }\nfunc file_primitive_proto_init() {\n\tif File_primitive_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_primitive_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*Primitive); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_primitive_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_primitive_proto_goTypes,\n\t\tDependencyIndexes: file_primitive_proto_depIdxs,\n\t\tEnumInfos:         file_primitive_proto_enumTypes,\n\t\tMessageInfos:      file_primitive_proto_msgTypes,\n\t}.Build()\n\tFile_primitive_proto = out.File\n\tfile_primitive_proto_rawDesc = nil\n\tfile_primitive_proto_goTypes = nil\n\tfile_primitive_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "graph/proto/primitive.proto",
    "content": "// Copyright 2016 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage proto;\n\noption go_package = \"github.com/cayleygraph/cayley/graph/proto\";\n\nmessage Primitive {\n  uint64 ID = 1;\n  uint64 Subject = 2;\n  uint64 Predicate = 3;\n  uint64 Object = 4;\n  uint64 Label = 5;\n  uint64 Replaces = 6;\n  int64 Timestamp = 7;\n  bytes Value = 8;\n  bool Deleted = 9;\n}\n\nenum PrimitiveType {\n  LINK = 0;\n  IRI = 1;\n  STRING = 2;\n  BNODE = 3;\n  TYPED_STR = 4;\n  LANG_STR = 5;\n  INT = 6;\n  FLOAT = 7;\n  BOOL = 8;\n  TIMESTAMP = 9;\n}\n"
  },
  {
    "path": "graph/proto/primitive_helpers.go",
    "content": "package proto\n\nimport \"github.com/cayleygraph/quad\"\n\n//go:generate protoc --go_opt=paths=source_relative --proto_path=. --go_out=. primitive.proto\n\nfunc (p *Primitive) GetDirection(d quad.Direction) uint64 {\n\tswitch d {\n\tcase quad.Subject:\n\t\treturn p.Subject\n\tcase quad.Predicate:\n\t\treturn p.Predicate\n\tcase quad.Object:\n\t\treturn p.Object\n\tcase quad.Label:\n\t\treturn p.Label\n\t}\n\tpanic(\"unknown direction\")\n}\n\nfunc (p *Primitive) SetDirection(d quad.Direction, v uint64) {\n\tswitch d {\n\tcase quad.Subject:\n\t\tp.Subject = v\n\tcase quad.Predicate:\n\t\tp.Predicate = v\n\tcase quad.Object:\n\t\tp.Object = v\n\tcase quad.Label:\n\t\tp.Label = v\n\t}\n}\n\nfunc (p *Primitive) IsNode() bool {\n\treturn len(p.Value) != 0\n}\n\nfunc (p *Primitive) Key() interface{} {\n\treturn p.ID\n}\n\nfunc (p *Primitive) IsSameLink(q *Primitive) bool {\n\treturn p.Subject == q.Subject && p.Predicate == q.Predicate && p.Object == q.Object && p.Label == q.Label\n}\n"
  },
  {
    "path": "graph/proto/serializations.pb.go",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.34.2\n// \tprotoc        v4.23.4\n// source: serializations.proto\n\npackage proto\n\nimport (\n\tpquads \"github.com/cayleygraph/quad/pquads\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype LogDelta struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tID        uint64       `protobuf:\"varint,1,opt,name=ID,proto3\" json:\"ID,omitempty\"`\n\tQuad      *pquads.Quad `protobuf:\"bytes,2,opt,name=Quad,proto3\" json:\"Quad,omitempty\"`\n\tAction    int32        `protobuf:\"varint,3,opt,name=Action,proto3\" json:\"Action,omitempty\"`\n\tTimestamp int64        `protobuf:\"varint,4,opt,name=Timestamp,proto3\" json:\"Timestamp,omitempty\"`\n}\n\nfunc (x *LogDelta) Reset() {\n\t*x = LogDelta{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_serializations_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *LogDelta) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*LogDelta) ProtoMessage() {}\n\nfunc (x *LogDelta) ProtoReflect() protoreflect.Message {\n\tmi := &file_serializations_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use LogDelta.ProtoReflect.Descriptor instead.\nfunc (*LogDelta) Descriptor() ([]byte, []int) {\n\treturn file_serializations_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *LogDelta) GetID() uint64 {\n\tif x != nil {\n\t\treturn x.ID\n\t}\n\treturn 0\n}\n\nfunc (x *LogDelta) GetQuad() *pquads.Quad {\n\tif x != nil {\n\t\treturn x.Quad\n\t}\n\treturn nil\n}\n\nfunc (x *LogDelta) GetAction() int32 {\n\tif x != nil {\n\t\treturn x.Action\n\t}\n\treturn 0\n}\n\nfunc (x *LogDelta) GetTimestamp() int64 {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn 0\n}\n\ntype HistoryEntry struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tHistory []uint64 `protobuf:\"varint,1,rep,packed,name=History,proto3\" json:\"History,omitempty\"`\n}\n\nfunc (x *HistoryEntry) Reset() {\n\t*x = HistoryEntry{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_serializations_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HistoryEntry) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HistoryEntry) ProtoMessage() {}\n\nfunc (x *HistoryEntry) ProtoReflect() protoreflect.Message {\n\tmi := &file_serializations_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HistoryEntry.ProtoReflect.Descriptor instead.\nfunc (*HistoryEntry) Descriptor() ([]byte, []int) {\n\treturn file_serializations_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HistoryEntry) GetHistory() []uint64 {\n\tif x != nil {\n\t\treturn x.History\n\t}\n\treturn nil\n}\n\ntype NodeData struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName  string        `protobuf:\"bytes,1,opt,name=Name,proto3\" json:\"Name,omitempty\"`\n\tSize  int64         `protobuf:\"varint,2,opt,name=Size,proto3\" json:\"Size,omitempty\"`\n\tValue *pquads.Value `protobuf:\"bytes,3,opt,name=value,proto3\" json:\"value,omitempty\"`\n}\n\nfunc (x *NodeData) Reset() {\n\t*x = NodeData{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_serializations_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *NodeData) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*NodeData) ProtoMessage() {}\n\nfunc (x *NodeData) ProtoReflect() protoreflect.Message {\n\tmi := &file_serializations_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use NodeData.ProtoReflect.Descriptor instead.\nfunc (*NodeData) Descriptor() ([]byte, []int) {\n\treturn file_serializations_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *NodeData) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *NodeData) GetSize() int64 {\n\tif x != nil {\n\t\treturn x.Size\n\t}\n\treturn 0\n}\n\nfunc (x *NodeData) GetValue() *pquads.Value {\n\tif x != nil {\n\t\treturn x.Value\n\t}\n\treturn nil\n}\n\nvar File_serializations_proto protoreflect.FileDescriptor\n\nvar file_serializations_proto_rawDesc = []byte{\n\t0x0a, 0x14, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x71,\n\t0x75, 0x61, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x72, 0x0a, 0x08, 0x4c, 0x6f,\n\t0x67, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x04, 0x52, 0x02, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x04, 0x51, 0x75, 0x61, 0x64, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x71, 0x75, 0x61, 0x64, 0x73, 0x2e, 0x51, 0x75,\n\t0x61, 0x64, 0x52, 0x04, 0x51, 0x75, 0x61, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69,\n\t0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,\n\t0x12, 0x1c, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20,\n\t0x01, 0x28, 0x03, 0x52, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x28,\n\t0x0a, 0x0c, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x18,\n\t0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52,\n\t0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x57, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65,\n\t0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x05,\n\t0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x71,\n\t0x75, 0x61, 0x64, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x63, 0x61, 0x79, 0x6c, 0x65, 0x79, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x63, 0x61, 0x79, 0x6c,\n\t0x65, 0x79, 0x2f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_serializations_proto_rawDescOnce sync.Once\n\tfile_serializations_proto_rawDescData = file_serializations_proto_rawDesc\n)\n\nfunc file_serializations_proto_rawDescGZIP() []byte {\n\tfile_serializations_proto_rawDescOnce.Do(func() {\n\t\tfile_serializations_proto_rawDescData = protoimpl.X.CompressGZIP(file_serializations_proto_rawDescData)\n\t})\n\treturn file_serializations_proto_rawDescData\n}\n\nvar file_serializations_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_serializations_proto_goTypes = []any{\n\t(*LogDelta)(nil),     // 0: proto.LogDelta\n\t(*HistoryEntry)(nil), // 1: proto.HistoryEntry\n\t(*NodeData)(nil),     // 2: proto.NodeData\n\t(*pquads.Quad)(nil),  // 3: pquads.Quad\n\t(*pquads.Value)(nil), // 4: pquads.Value\n}\nvar file_serializations_proto_depIdxs = []int32{\n\t3, // 0: proto.LogDelta.Quad:type_name -> pquads.Quad\n\t4, // 1: proto.NodeData.value:type_name -> pquads.Value\n\t2, // [2:2] is the sub-list for method output_type\n\t2, // [2:2] is the sub-list for method input_type\n\t2, // [2:2] is the sub-list for extension type_name\n\t2, // [2:2] is the sub-list for extension extendee\n\t0, // [0:2] is the sub-list for field type_name\n}\n\nfunc init() { file_serializations_proto_init() }\nfunc file_serializations_proto_init() {\n\tif File_serializations_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_serializations_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*LogDelta); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_serializations_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HistoryEntry); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_serializations_proto_msgTypes[2].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*NodeData); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_serializations_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_serializations_proto_goTypes,\n\t\tDependencyIndexes: file_serializations_proto_depIdxs,\n\t\tMessageInfos:      file_serializations_proto_msgTypes,\n\t}.Build()\n\tFile_serializations_proto = out.File\n\tfile_serializations_proto_rawDesc = nil\n\tfile_serializations_proto_goTypes = nil\n\tfile_serializations_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "graph/proto/serializations.proto",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage proto;\n\noption go_package = \"github.com/cayleygraph/cayley/graph/proto\";\n\nimport \"quads.proto\";\n\nmessage LogDelta {\n  uint64 ID = 1;\n  pquads.Quad Quad = 2;\n  int32 Action = 3;\n  int64 Timestamp = 4;\n}\n\nmessage HistoryEntry {\n  repeated uint64 History = 1;\n}\n\nmessage NodeData {\n  string Name = 1;\n  int64 Size = 2;\n  pquads.Value value = 3;\n}\n"
  },
  {
    "path": "graph/proto/serializations_helpers.go",
    "content": "package proto\n\nimport (\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n)\n\n//go:generate curl -LO https://github.com/cayleygraph/quad/raw/v1.3.0/pquads/quads.proto\n//go:generate protoc --go_opt=paths=source_relative --proto_path=. --go_out=. serializations.proto\n\n// GetNativeValue returns the value stored in Node.\nfunc (m *NodeData) GetNativeValue() quad.Value {\n\tif m == nil {\n\t\treturn nil\n\t} else if m.Value == nil {\n\t\tif m.Name == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\treturn quad.Raw(m.Name)\n\t}\n\treturn m.Value.ToNative()\n}\n\nfunc (m *NodeData) Upgrade() {\n\tif m.Value == nil {\n\t\tm.Value = pquads.MakeValue(quad.Raw(m.Name))\n\t\tm.Name = \"\"\n\t}\n}\n"
  },
  {
    "path": "graph/quadstore.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\n// Defines the QuadStore interface. Every backing store must implement at\n// least this interface.\n//\n// Most of these are pretty straightforward. As long as we can surface this\n// interface, the rest of the stack will \"just work\" and we can connect to any\n// quad backing store we prefer.\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// Ref defines an opaque \"quad store reference\" type. However the backend wishes\n// to implement it, a Ref is merely a token to a quad or a node that the\n// backing store itself understands, and the base iterators pass around.\n//\n// For example, in a very traditional, graphd-style graph, these are int64s\n// (guids of the primitives). In a very direct sort of graph, these could be\n// pointers to structs, or merely quads, or whatever works best for the\n// backing store.\n//\n// These must be comparable, or return a comparable version on Key.\ntype Ref = refs.Ref\n\nfunc ValuesOf(ctx context.Context, qs refs.Namer, vals []Ref) ([]quad.Value, error) {\n\treturn refs.ValuesOf(ctx, qs, vals)\n}\n\nfunc RefsOf(ctx context.Context, qs refs.Namer, nodes []quad.Value) ([]Ref, error) {\n\treturn refs.RefsOf(ctx, qs, nodes)\n}\n\ntype QuadIndexer interface {\n\t// Given an opaque token, returns the quad for that token from the store.\n\tQuad(Ref) (quad.Quad, error)\n\n\t// Given a direction and a token, creates an iterator of links which have\n\t// that node token in that directional field.\n\tQuadIterator(quad.Direction, Ref) iterator.Shape\n\n\t// QuadIteratorSize returns an estimated size of an iterator.\n\tQuadIteratorSize(ctx context.Context, d quad.Direction, v Ref) (refs.Size, error)\n\n\t// Convenience function for speed. Given a quad token and a direction\n\t// return the node token for that direction. Sometimes, a QuadStore\n\t// can do this without going all the way to the backing store, and\n\t// gives the QuadStore the opportunity to make this optimization.\n\t//\n\t// Iterators will call this. At worst, a valid implementation is\n\t//\n\t//  qs.ValueOf(qs.Quad(id).Get(dir))\n\t//\n\tQuadDirection(id Ref, d quad.Direction) (Ref, error)\n\n\t// Stats returns the number of nodes and quads currently stored.\n\t// Exact flag controls the correctness of the value. It can be an estimation, or a precise calculation.\n\t// The quadstore may have a fast way of retrieving the precise stats, in this case it may ignore 'exact'\n\t// flag and always return correct stats (with an appropriate flags set in the output).\n\tStats(ctx context.Context, exact bool) (Stats, error)\n}\n\n// Stats of a graph.\ntype Stats struct {\n\tNodes refs.Size // number of nodes\n\tQuads refs.Size // number of quads\n}\n\ntype QuadStore interface {\n\trefs.Namer\n\tQuadIndexer\n\n\t// The only way in is through building a transaction, which\n\t// is done by a replication strategy.\n\tApplyDeltas(in []Delta, opts IgnoreOpts) error\n\n\t// NewQuadWriter starts a batch quad import process.\n\t// The order of changes is not guaranteed, neither is the order and result of concurrent ApplyDeltas.\n\tNewQuadWriter() (quad.WriteCloser, error)\n\n\t// Returns an iterator enumerating all nodes in the graph.\n\tNodesAllIterator() iterator.Shape\n\n\t// Returns an iterator enumerating all links in the graph.\n\tQuadsAllIterator() iterator.Shape\n\n\t// Close the quad store and clean up. (Flush to disk, cleanly\n\t// sever connections, etc)\n\tClose() error\n}\n\ntype Options map[string]interface{}\n\nvar (\n\ttypeInt = reflect.TypeOf(int(0))\n)\n\nfunc (d Options) IntKey(key string, def int) (int, error) {\n\tif val, ok := d[key]; ok {\n\t\tif reflect.TypeOf(val).ConvertibleTo(typeInt) {\n\t\t\ti := reflect.ValueOf(val).Convert(typeInt).Int()\n\t\t\treturn int(i), nil\n\t\t}\n\n\t\treturn def, fmt.Errorf(\"Invalid %s parameter type from config: %T\", key, val)\n\t}\n\treturn def, nil\n}\n\nfunc (d Options) StringKey(key string, def string) (string, error) {\n\tif val, ok := d[key]; ok {\n\t\tif v, ok := val.(string); ok {\n\t\t\treturn v, nil\n\t\t}\n\n\t\treturn def, fmt.Errorf(\"Invalid %s parameter type from config: %T\", key, val)\n\t}\n\n\treturn def, nil\n}\n\nfunc (d Options) BoolKey(key string, def bool) (bool, error) {\n\tif val, ok := d[key]; ok {\n\t\tif v, ok := val.(bool); ok {\n\t\t\treturn v, nil\n\t\t}\n\n\t\treturn def, fmt.Errorf(\"Invalid %s parameter type from config: %T\", key, val)\n\t}\n\n\treturn def, nil\n}\n\nvar (\n\tErrDatabaseExists = errors.New(\"quadstore: cannot init; database already exists\")\n\tErrNotInitialized = errors.New(\"quadstore: not initialized\")\n)\n"
  },
  {
    "path": "graph/quadwriter.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\n// Defines the interface for consistent replication of a graph instance.\n//\n// Separate from the backend, this dictates how individual quads get\n// identified and replicated consistently across (potentially) multiple\n// instances. The simplest case is to keep an append-only log of quad\n// changes.\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\ntype Procedure int8\n\nfunc (p Procedure) String() string {\n\tswitch p {\n\tcase +1:\n\t\treturn \"add\"\n\tcase -1:\n\t\treturn \"delete\"\n\tdefault:\n\t\treturn \"invalid\"\n\t}\n}\n\n// The different types of actions a transaction can do.\nconst (\n\tAdd    Procedure = +1\n\tDelete Procedure = -1\n)\n\ntype Delta struct {\n\tQuad   quad.Quad\n\tAction Procedure\n}\n\n// Unwrap returns an original QuadStore value if it was wrapped by Handle.\n// This prevents shadowing of optional interface implementations.\nfunc Unwrap(qs QuadStore) QuadStore {\n\tif h, ok := qs.(*Handle); ok {\n\t\treturn h.QuadStore\n\t}\n\treturn qs\n}\n\ntype Handle struct {\n\tQuadStore\n\tQuadWriter\n}\n\ntype IgnoreOpts struct {\n\tIgnoreDup, IgnoreMissing bool\n}\n\nfunc (h *Handle) Close() error {\n\terr := h.QuadWriter.Close()\n\th.QuadStore.Close()\n\treturn err\n}\n\nvar (\n\tErrQuadExists    = errors.New(\"quad exists\")\n\tErrQuadNotExist  = errors.New(\"quad does not exist\")\n\tErrInvalidAction = errors.New(\"invalid action\")\n\tErrNodeNotExists = errors.New(\"node does not exist\")\n)\n\n// DeltaError records an error and the delta that caused it.\ntype DeltaError struct {\n\tDelta Delta\n\tErr   error\n}\n\nfunc (e *DeltaError) Error() string {\n\tif !e.Delta.Quad.IsValid() {\n\t\treturn e.Err.Error()\n\t}\n\treturn e.Delta.Action.String() + \" \" + e.Delta.Quad.String() + \": \" + e.Err.Error()\n}\n\nfunc (e *DeltaError) Unwrap() error {\n\treturn e.Err\n}\n\n// IsQuadExist returns whether an error is a DeltaError\n// with the Err field equal to ErrQuadExists.\nfunc IsQuadExist(err error) bool {\n\treturn errors.Is(err, ErrQuadExists)\n}\n\n// IsQuadNotExist returns whether an error is a DeltaError\n// with the Err field equal to ErrQuadNotExist.\nfunc IsQuadNotExist(err error) bool {\n\treturn errors.Is(err, ErrQuadNotExist)\n}\n\n// IsInvalidAction returns whether an error is a DeltaError\n// with the Err field equal to ErrInvalidAction.\nfunc IsInvalidAction(err error) bool {\n\treturn errors.Is(err, ErrInvalidAction)\n}\n\nvar (\n\t// IgnoreDuplicates specifies whether duplicate quads\n\t// cause an error during loading or are ignored.\n\tIgnoreDuplicates = true\n\n\t// IgnoreMissing specifies whether missing quads\n\t// cause an error during deletion or are ignored.\n\tIgnoreMissing = false\n)\n\ntype QuadWriter interface {\n\t// AddQuad adds a quad to the store.\n\tAddQuad(quad.Quad) error\n\n\t// TODO(barakmich): Deprecate in favor of transaction.\n\t// AddQuadSet adds a set of quads to the store, atomically if possible.\n\tAddQuadSet([]quad.Quad) error\n\n\t// RemoveQuad removes a quad matching the given one  from the database,\n\t// if it exists. Does nothing otherwise.\n\tRemoveQuad(quad.Quad) error\n\n\t// ApplyTransaction applies a set of quad changes.\n\tApplyTransaction(*Transaction) error\n\n\t// RemoveNode removes all quads which have the given node as subject, predicate, object, or label.\n\t//\n\t// It returns ErrNodeNotExists if node is missing.\n\tRemoveNode(quad.Value) error\n\n\t// Close cleans up replication and closes the writing aspect of the database.\n\tClose() error\n}\n\ntype NewQuadWriterFunc func(QuadStore, Options) (QuadWriter, error)\n\nvar writerRegistry = make(map[string]NewQuadWriterFunc)\n\nfunc RegisterWriter(name string, newFunc NewQuadWriterFunc) {\n\tif _, found := writerRegistry[name]; found {\n\t\tpanic(\"already registered QuadWriter \" + name)\n\t}\n\twriterRegistry[name] = newFunc\n}\n\nfunc NewQuadWriter(name string, qs QuadStore, opts Options) (QuadWriter, error) {\n\tnewFunc, hasNew := writerRegistry[name]\n\tif !hasNew {\n\t\treturn nil, errors.New(\"replication: name '\" + name + \"' is not registered\")\n\t}\n\treturn newFunc(qs, opts)\n}\n\nfunc WriterMethods() []string {\n\tt := make([]string, 0, len(writerRegistry))\n\tfor n := range writerRegistry {\n\t\tt = append(t, n)\n\t}\n\treturn t\n}\n\ntype BatchWriter interface {\n\tquad.WriteCloser\n\tFlush() error\n}\n\n// NewWriter creates a quad writer for a given QuadStore.\n//\n// Caller must call Flush or Close to flush an internal buffer.\nfunc NewWriter(qs QuadWriter) BatchWriter {\n\treturn &batchWriter{qs: qs}\n}\n\ntype batchWriter struct {\n\tqs  QuadWriter\n\tbuf []quad.Quad\n}\n\nfunc (w *batchWriter) flushBuffer(force bool) error {\n\tif !force && len(w.buf) < quad.DefaultBatch {\n\t\treturn nil\n\t}\n\t_, err := w.WriteQuads(w.buf)\n\tw.buf = w.buf[:0]\n\treturn err\n}\n\nfunc (w *batchWriter) WriteQuad(q quad.Quad) error {\n\tif err := w.flushBuffer(false); err != nil {\n\t\treturn err\n\t}\n\tw.buf = append(w.buf, q)\n\treturn nil\n}\nfunc (w *batchWriter) WriteQuads(quads []quad.Quad) (int, error) {\n\tif err := w.qs.AddQuadSet(quads); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(quads), nil\n}\nfunc (w *batchWriter) Flush() error {\n\treturn w.flushBuffer(true)\n}\nfunc (w *batchWriter) Close() error {\n\treturn w.Flush()\n}\n\n// NewTxWriter creates a writer that applies a given procedures for all quads in stream.\n// If procedure is zero, Add operation will be used.\nfunc NewTxWriter(tx *Transaction, p Procedure) quad.Writer {\n\tif p == 0 {\n\t\tp = Add\n\t}\n\treturn &txWriter{tx: tx, p: p}\n}\n\ntype txWriter struct {\n\ttx *Transaction\n\tp  Procedure\n}\n\nfunc (w *txWriter) WriteQuad(q quad.Quad) error {\n\tswitch w.p {\n\tcase Add:\n\t\tw.tx.AddQuad(q)\n\tcase Delete:\n\t\tw.tx.RemoveQuad(q)\n\tdefault:\n\t\treturn ErrInvalidAction\n\t}\n\treturn nil\n}\n\nfunc (w *txWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\tfor i, q := range buf {\n\t\tif err := w.WriteQuad(q); err != nil {\n\t\t\treturn i, err\n\t\t}\n\t}\n\treturn len(buf), nil\n}\n\n// NewRemover creates a quad writer for a given QuadStore which removes quads instead of adding them.\nfunc NewRemover(qs QuadWriter) BatchWriter {\n\treturn &removeWriter{qs: qs}\n}\n\ntype removeWriter struct {\n\tqs QuadWriter\n}\n\nfunc (w *removeWriter) WriteQuad(q quad.Quad) error {\n\treturn w.qs.RemoveQuad(q)\n}\nfunc (w *removeWriter) WriteQuads(quads []quad.Quad) (int, error) {\n\ttx := NewTransaction()\n\tfor _, q := range quads {\n\t\ttx.RemoveQuad(q)\n\t}\n\tif err := w.qs.ApplyTransaction(tx); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(quads), nil\n}\nfunc (w *removeWriter) Flush() error {\n\treturn nil // TODO: batch deletes automatically\n}\nfunc (w *removeWriter) Close() error { return nil }\n\n// NewQuadStoreReader creates a quad reader for a given QuadStore.\nfunc NewQuadStoreReader(qs QuadStore) quad.ReadSkipCloser {\n\treturn NewResultReader(qs, nil)\n}\n\n// NewResultReader creates a quad reader for a given QuadStore and iterator.\n// If iterator is nil QuadsAllIterator will be used.\n//\n// Only quads returned by iterator's Result will be used.\n//\n// Iterator will be closed with the reader.\nfunc NewResultReader(qs QuadStore, it iterator.Scanner) quad.ReadSkipCloser {\n\tif it == nil {\n\t\tit = qs.QuadsAllIterator().Iterate()\n\t}\n\treturn &quadReader{qs: qs, it: it}\n}\n\ntype quadReader struct {\n\tqs QuadStore\n\tit iterator.Scanner\n}\n\nfunc (r *quadReader) ReadQuad() (quad.Quad, error) {\n\tif r.it.Next(context.TODO()) {\n\t\treturn r.qs.Quad(r.it.Result())\n\t}\n\terr := r.it.Err()\n\tif err == nil {\n\t\terr = io.EOF\n\t}\n\treturn quad.Quad{}, err\n}\nfunc (r *quadReader) SkipQuad() error {\n\tif r.it.Next(context.TODO()) {\n\t\treturn nil\n\t}\n\tif err := r.it.Err(); err != nil {\n\t\treturn err\n\t}\n\treturn io.EOF\n}\nfunc (r *quadReader) Close() error { return r.it.Close() }\n"
  },
  {
    "path": "graph/quadwriter_test.go",
    "content": "package graph\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestIsQuadExist(t *testing.T) {\n\ttests := []struct {\n\t\tErr     error\n\t\tMatches bool\n\t}{\n\t\t{Err: nil, Matches: false},\n\t\t{Err: errors.New(\"foo\"), Matches: false},\n\t\t{Err: ErrQuadExists, Matches: true},\n\t\t{Err: &DeltaError{Err: errors.New(\"foo\")}, Matches: false},\n\t\t{Err: &DeltaError{Err: ErrQuadExists}, Matches: true},\n\t}\n\n\tfor i, test := range tests {\n\t\tif match := IsQuadExist(test.Err); test.Matches != match {\n\t\t\tt.Errorf(\"%d> unexpected match: %t\", i, match)\n\t\t}\n\t}\n}\n\nfunc TestIsQuadNotExist(t *testing.T) {\n\ttests := []struct {\n\t\tErr     error\n\t\tMatches bool\n\t}{\n\t\t{Err: nil, Matches: false},\n\t\t{Err: errors.New(\"foo\"), Matches: false},\n\t\t{Err: ErrQuadNotExist, Matches: true},\n\t\t{Err: &DeltaError{Err: errors.New(\"foo\")}, Matches: false},\n\t\t{Err: &DeltaError{Err: ErrQuadNotExist}, Matches: true},\n\t}\n\n\tfor i, test := range tests {\n\t\tif match := IsQuadNotExist(test.Err); test.Matches != match {\n\t\t\tt.Errorf(\"%d> unexpected match: %t\", i, match)\n\t\t}\n\t}\n}\n\nfunc TestIsInvalidAction(t *testing.T) {\n\ttests := []struct {\n\t\tErr     error\n\t\tMatches bool\n\t}{\n\t\t{Err: nil, Matches: false},\n\t\t{Err: errors.New(\"foo\"), Matches: false},\n\t\t{Err: ErrInvalidAction, Matches: true},\n\t\t{Err: &DeltaError{Err: errors.New(\"foo\")}, Matches: false},\n\t\t{Err: &DeltaError{Err: ErrInvalidAction}, Matches: true},\n\t}\n\n\tfor i, test := range tests {\n\t\tif match := IsInvalidAction(test.Err); test.Matches != match {\n\t\t\tt.Errorf(\"%d> unexpected match: %t\", i, match)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "graph/refs/refs.go",
    "content": "package refs\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/quad\"\n)\n\n// Size of a graph (either in nodes or quads).\ntype Size struct {\n\tValue int64\n\tExact bool\n}\n\n// Ref defines an opaque \"quad store reference\" type. However the backend wishes\n// to implement it, a Ref is merely a token to a quad or a node that the\n// backing store itself understands, and the base iterators pass around.\n//\n// For example, in a very traditional, graphd-style graph, these are int64s\n// (guids of the primitives). In a very direct sort of graph, these could be\n// pointers to structs, or merely quads, or whatever works best for the\n// backing store.\n//\n// These must be comparable, or return a comparable version on Key.\ntype Ref interface {\n\t// Key returns a dynamic type that is comparable according to the Go language specification.\n\t// The returned value must be unique for each receiver value.\n\tKey() interface{}\n}\n\ntype Namer interface {\n\t// Given a node ID, return the opaque token used by the QuadStore\n\t// to represent that id. Returns nil, nil on not found.\n\tValueOf(quad.Value) (Ref, error)\n\t// Given an opaque token, return the node that it represents.\n\t// Returns nil, nil on not found.\n\tNameOf(Ref) (quad.Value, error)\n}\n\ntype BatchNamer interface {\n\tValuesOf(ctx context.Context, vals []Ref) ([]quad.Value, error)\n\tRefsOf(ctx context.Context, nodes []quad.Value) ([]Ref, error)\n}\n\nfunc HashOf(s quad.Value) (out ValueHash) {\n\tif s == nil {\n\t\treturn\n\t}\n\tquad.HashTo(s, out[:])\n\treturn\n}\n\nvar _ Ref = ValueHash{}\n\n// ValueHash is a hash of a single value.\ntype ValueHash [quad.HashSize]byte\n\nfunc (h ValueHash) Valid() bool {\n\treturn h != ValueHash{}\n}\nfunc (h ValueHash) Key() interface{} { return h }\nfunc (h ValueHash) String() string {\n\tif !h.Valid() {\n\t\treturn \"\"\n\t}\n\treturn hex.EncodeToString(h[:])\n}\n\n// PreFetchedValue is an optional interface for graph.Ref to indicate that\n// quadstore has already loaded a value into memory.\ntype PreFetchedValue interface {\n\tRef\n\tNameOf() quad.Value\n}\n\nfunc PreFetched(v quad.Value) PreFetchedValue {\n\treturn fetchedValue{v}\n}\n\ntype fetchedValue struct {\n\tVal quad.Value\n}\n\nfunc (v fetchedValue) IsNode() bool       { return true }\nfunc (v fetchedValue) NameOf() quad.Value { return v.Val }\nfunc (v fetchedValue) Key() interface{}   { return v.Val }\n\n// ToKey prepares Ref to be stored inside maps, calling Key() if necessary.\nfunc ToKey(v Ref) interface{} {\n\tif v == nil {\n\t\treturn nil\n\t}\n\treturn v.Key()\n}\n\nvar _ Ref = QuadHash{}\n\ntype QuadHash struct {\n\tSubject   ValueHash\n\tPredicate ValueHash\n\tObject    ValueHash\n\tLabel     ValueHash\n}\n\nfunc (q QuadHash) Dirs() [4]ValueHash {\n\treturn [4]ValueHash{\n\t\tq.Subject,\n\t\tq.Predicate,\n\t\tq.Object,\n\t\tq.Label,\n\t}\n}\nfunc (q QuadHash) Key() interface{} { return q }\nfunc (q QuadHash) Get(d quad.Direction) ValueHash {\n\tswitch d {\n\tcase quad.Subject:\n\t\treturn q.Subject\n\tcase quad.Predicate:\n\t\treturn q.Predicate\n\tcase quad.Object:\n\t\treturn q.Object\n\tcase quad.Label:\n\t\treturn q.Label\n\t}\n\tpanic(fmt.Errorf(\"unknown direction: %v\", d))\n}\nfunc (q *QuadHash) Set(d quad.Direction, h ValueHash) {\n\tswitch d {\n\tcase quad.Subject:\n\t\tq.Subject = h\n\tcase quad.Predicate:\n\t\tq.Predicate = h\n\tcase quad.Object:\n\t\tq.Object = h\n\tcase quad.Label:\n\t\tq.Label = h\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unknown direction: %v\", d))\n\t}\n}\n\nfunc ValuesOf(ctx context.Context, qs Namer, vals []Ref) ([]quad.Value, error) {\n\tif bq, ok := qs.(BatchNamer); ok {\n\t\treturn bq.ValuesOf(ctx, vals)\n\t}\n\tout := make([]quad.Value, len(vals))\n\tvar err error\n\tfor i, v := range vals {\n\t\tout[i], err = qs.NameOf(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn out, nil\n}\n\nfunc RefsOf(ctx context.Context, qs Namer, nodes []quad.Value) ([]Ref, error) {\n\tif bq, ok := qs.(BatchNamer); ok {\n\t\treturn bq.RefsOf(ctx, nodes)\n\t}\n\tvalues := make([]Ref, len(nodes))\n\tfor i, node := range nodes {\n\t\tvalue, err := qs.ValueOf(node)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif value == nil {\n\t\t\treturn nil, fmt.Errorf(\"not found: %v\", node)\n\t\t}\n\t\tvalues[i] = value\n\t}\n\treturn values, nil\n}\n"
  },
  {
    "path": "graph/registry.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nvar (\n\tErrQuadStoreNotRegistred  = fmt.Errorf(\"this QuadStore is not registered\")\n\tErrQuadStoreNotPersistent = fmt.Errorf(\"cannot specify address for non-persistent backend\")\n\tErrOperationNotSupported  = fmt.Errorf(\"this Operation is not supported\")\n)\n\nvar storeRegistry = make(map[string]QuadStoreRegistration)\n\ntype NewStoreFunc func(string, Options) (QuadStore, error)\ntype InitStoreFunc func(string, Options) error\ntype UpgradeStoreFunc func(string, Options) error\n\ntype QuadStoreRegistration struct {\n\tNewFunc      NewStoreFunc\n\tUpgradeFunc  UpgradeStoreFunc\n\tInitFunc     InitStoreFunc\n\tIsPersistent bool\n}\n\nfunc RegisterQuadStore(name string, register QuadStoreRegistration) {\n\tif register.NewFunc == nil {\n\t\tpanic(\"NewFunc must not be nil\")\n\t}\n\n\t// Register QuadStore with friendly name\n\tif _, found := storeRegistry[name]; found {\n\t\tpanic(fmt.Sprintf(\"Already registered QuadStore %q.\", name))\n\t}\n\tstoreRegistry[name] = register\n}\n\nfunc NewQuadStore(name string, dbpath string, opts Options) (QuadStore, error) {\n\tr, registered := storeRegistry[name]\n\tif !registered {\n\t\treturn nil, ErrQuadStoreNotRegistred\n\t} else if dbpath != \"\" && !r.IsPersistent {\n\t\treturn nil, ErrQuadStoreNotPersistent\n\t}\n\treturn r.NewFunc(dbpath, opts)\n}\n\nfunc InitQuadStore(name string, dbpath string, opts Options) error {\n\tr, registered := storeRegistry[name]\n\tif !registered {\n\t\treturn ErrQuadStoreNotRegistred\n\t} else if r.InitFunc == nil {\n\t\treturn ErrOperationNotSupported\n\t}\n\treturn r.InitFunc(dbpath, opts)\n}\n\nfunc UpgradeQuadStore(name string, dbpath string, opts Options) error {\n\tr, registered := storeRegistry[name]\n\tif !registered {\n\t\treturn ErrQuadStoreNotRegistred\n\t} else if r.UpgradeFunc == nil {\n\t\t// return ErrOperationNotSupported\n\t\treturn nil\n\t}\n\treturn r.UpgradeFunc(dbpath, opts)\n}\n\nfunc IsRegistered(name string) bool {\n\t_, ok := storeRegistry[name]\n\treturn ok\n}\n\nfunc IsPersistent(name string) bool {\n\treturn storeRegistry[name].IsPersistent\n}\n\nfunc QuadStores() []string {\n\tt := make([]string, 0, len(storeRegistry))\n\tfor n := range storeRegistry {\n\t\tt = append(t, n)\n\t}\n\tsort.Strings(t)\n\treturn t\n}\n"
  },
  {
    "path": "graph/sql/cockroach/cockroach.go",
    "content": "package cockroach\n\nimport (\n\t\"bytes\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\" // registers \"pgx\" driver\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n\tcsql \"github.com/cayleygraph/cayley/graph/sql\"\n)\n\nconst Type = \"cockroach\"\n\nfunc init() {\n\tcsql.Register(Type, csql.Registration{\n\t\tDriver:      \"pgx\",\n\t\tHashType:    `BYTEA`,\n\t\tBytesType:   `BYTEA`,\n\t\tHorizonType: `BIGSERIAL`,\n\t\tTimeType:    `timestamp with time zone`,\n\t\tNodesTableExtra: `\n\tFAMILY fhash (hash),\n\tFAMILY frefs (refs),\n\tFAMILY fvalue (value, value_string, datatype, language, iri, bnode,\n\t\tvalue_int, value_bool, value_float, value_time)\n`,\n\t\tQueryDialect: csql.QueryDialect{\n\t\t\tRegexpOp: \"~\",\n\t\t\tFieldQuote: func(name string) string {\n\t\t\t\treturn pgx.Identifier{name}.Sanitize()\n\t\t\t},\n\t\t\tPlaceholder: func(n int) string {\n\t\t\t\treturn fmt.Sprintf(\"$%d\", n)\n\t\t\t},\n\t\t},\n\t\tNoForeignKeys: true,\n\t\tError:         convError,\n\t\t//Estimated: func(table string) string{\n\t\t//\treturn \"SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname='\"+table+\"';\"\n\t\t//},\n\t\tRunTx:               runTxCockroach,\n\t\tTxRetry:             retryTxCockroach,\n\t\tNoSchemaChangesInTx: true,\n\t})\n}\n\n// AmbiguousCommitError represents an error that left a transaction in an\n// ambiguous state: unclear if it committed or not.\ntype AmbiguousCommitError struct {\n\tErr error\n}\n\nfunc (e *AmbiguousCommitError) Error() string {\n\treturn e.Err.Error()\n}\n\nfunc (e *AmbiguousCommitError) Unwrap() error {\n\treturn e.Err\n}\n\n// retryTxCockroach runs the transaction and will retry in case of a retryable error.\n// https://www.cockroachlabs.com/docs/transactions.html#client-side-transaction-retries\nfunc retryTxCockroach(tx *sql.Tx, stmts func() error) error {\n\t// Specify that we intend to retry this txn in case of CockroachDB retryable\n\t// errors.\n\tif _, err := tx.Exec(\"SAVEPOINT cockroach_restart\"); err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\treleased := false\n\n\t\terr := stmts()\n\n\t\tif err == nil {\n\t\t\t// RELEASE acts like COMMIT in CockroachDB. We use it since it gives us an\n\t\t\t// opportunity to react to retryable errors, whereas tx.Commit() doesn't.\n\t\t\treleased = true\n\t\t\tif _, err = tx.Exec(\"RELEASE SAVEPOINT cockroach_restart\"); err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\t// We got an error; let's see if it's a retryable one and, if so, restart. We look\n\t\t// for either the standard PG errcode SerializationFailureError:40001 or the Cockroach extension\n\t\t// errcode RetriableError:CR000. The Cockroach extension has been removed server-side, but support\n\t\t// for it has been left here for now to maintain backwards compatibility.\n\t\tvar pgErr *pgconn.PgError\n\t\tif !errors.As(err, &pgErr) {\n\t\t\treturn err\n\t\t}\n\t\tif retryable := pgErr.Code == \"CR000\" || pgErr.Code == \"40001\"; !retryable {\n\t\t\tif released {\n\t\t\t\terr = &AmbiguousCommitError{err}\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif _, err = tx.Exec(\"ROLLBACK TO SAVEPOINT cockroach_restart\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc convError(err error) error {\n\tvar e *pgconn.PgError\n\tif !errors.As(err, &e) {\n\t\treturn err\n\t}\n\tswitch e.Code {\n\tcase \"42P07\":\n\t\treturn graph.ErrDatabaseExists\n\t}\n\treturn err\n}\n\nfunc convInsertError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tvar pe *pgconn.PgError\n\tif errors.As(err, &pe) {\n\t\tif pe.Code == \"23505\" {\n\t\t\t// TODO: reference to delta\n\t\t\treturn &graph.DeltaError{Err: graph.ErrQuadExists}\n\t\t}\n\t}\n\treturn err\n}\n\n// runTxCockroach performs the node and quad updates in the provided transaction.\n// This is based on ../postgres/postgres.go:RunTx, but focuses on doing fewer insert statements,\n// since those are comparatively expensive for CockroachDB.\nfunc runTxCockroach(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error {\n\t// First, compile the sets of nodes, split by csql.ValueType.\n\t// Each of those will require a separate INSERT statement.\n\ttype nodeEntry struct {\n\t\trefInc int\n\t\tvalues []interface{} // usually two, but sometimes three elements (includes hash)\n\t}\n\tnodeEntries := make(map[csql.ValueType][]nodeEntry)\n\tfor _, n := range nodes {\n\t\tif n.RefInc < 0 {\n\t\t\tpanic(\"unexpected node update\")\n\t\t}\n\t\tnodeType, values, err := csql.NodeValues(csql.NodeHash{ValueHash: n.Hash}, n.Val)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnodeEntries[nodeType] = append(nodeEntries[nodeType], nodeEntry{\n\t\t\trefInc: n.RefInc,\n\t\t\tvalues: values,\n\t\t})\n\t}\n\n\t// Next, build and execute the INSERT statements for each type.\n\tfor nodeType, entries := range nodeEntries {\n\t\tvar query bytes.Buffer\n\t\tvar allValues []interface{}\n\t\tvalCols := nodeType.Columns()\n\t\tfmt.Fprintf(&query, \"INSERT INTO nodes (refs, hash, %s) VALUES \", strings.Join(valCols, \", \"))\n\t\tph := 1 // next placeholder counter\n\t\tfor i, entry := range entries {\n\t\t\tif i > 0 {\n\t\t\t\tfmt.Fprint(&query, \", \")\n\t\t\t}\n\t\t\tfmt.Fprint(&query, \"(\")\n\t\t\t// sanity check\n\t\t\tif len(entry.values) != 1+len(valCols) { // +1 for hash, which is in values\n\t\t\t\tpanic(fmt.Sprintf(\"internal error: %d entry values vs. %d value columns\", len(entry.values), len(valCols)))\n\t\t\t}\n\t\t\tfor j := 0; j < 1+len(entry.values); j++ { // +1 for refs\n\t\t\t\tif j > 0 {\n\t\t\t\t\tfmt.Fprint(&query, \", \")\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(&query, \"$%d\", ph)\n\t\t\t\tph++\n\t\t\t}\n\t\t\tfmt.Fprint(&query, \")\")\n\t\t\tallValues = append(allValues, entry.refInc)\n\t\t\tallValues = append(allValues, entry.values...)\n\t\t}\n\t\tfmt.Fprint(&query, \" ON CONFLICT (hash) DO UPDATE SET refs = nodes.refs + EXCLUDED.refs RETURNING NOTHING;\")\n\t\t_, err := tx.Exec(query.String(), allValues...)\n\t\terr = convInsertError(err)\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"couldn't exec node INSERT statement [%s]: %v\", query.String(), err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Now do the same thing with quads.\n\t// It is simpler because there's only one composite type to insert,\n\t// so only one INSERT statement is required.\n\tif len(quads) == 0 {\n\t\treturn nil\n\t}\n\n\tvar query bytes.Buffer\n\tvar allValues []interface{}\n\tfmt.Fprintf(&query, \"INSERT INTO quads (subject_hash, predicate_hash, object_hash, label_hash, ts) VALUES \")\n\tfor i, d := range quads {\n\t\tif d.Del {\n\t\t\tpanic(\"unexpected quad delete\")\n\t\t}\n\t\tif i > 0 {\n\t\t\tfmt.Fprint(&query, \", \")\n\t\t}\n\t\tfmt.Fprintf(&query, \"($%d, $%d, $%d, $%d, now())\", 4*i+1, 4*i+2, 4*i+3, 4*i+4)\n\t\tallValues = append(allValues,\n\t\t\tcsql.NodeHash{ValueHash: d.Quad.Subject}.SQLValue(),\n\t\t\tcsql.NodeHash{ValueHash: d.Quad.Predicate}.SQLValue(),\n\t\t\tcsql.NodeHash{ValueHash: d.Quad.Object}.SQLValue(),\n\t\t\tcsql.NodeHash{ValueHash: d.Quad.Label}.SQLValue())\n\t}\n\tif opts.IgnoreDup {\n\t\tfmt.Fprint(&query, \" ON CONFLICT (subject_hash, predicate_hash, object_hash) DO NOTHING\")\n\t\t// Only use RETURNING NOTHING when we're ignoring duplicates;\n\t\t// otherwise the error returned on duplicates will be different.\n\t\tfmt.Fprint(&query, \" RETURNING NOTHING\")\n\t}\n\tfmt.Fprint(&query, \";\")\n\t_, err := tx.Exec(query.String(), allValues...)\n\terr = convInsertError(err)\n\tif err != nil {\n\t\tif _, ok := err.(*graph.DeltaError); !ok {\n\t\t\tclog.Errorf(\"couldn't exec quad INSERT statement [%s]: %v\", query.String(), err)\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "graph/sql/cockroach/cockroach_test.go",
    "content": "//go:build docker\n// +build docker\n\npackage cockroach\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"net\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/sql/sqltest\"\n\t\"github.com/cayleygraph/cayley/internal/dock\"\n)\n\nfunc makeCockroach(t testing.TB) (string, graph.Options) {\n\tvar conf dock.Config\n\n\tconf.Image = \"cockroachdb/cockroach:latest-v24.1\"\n\tconf.Cmd = []string{\"start-single-node\", \"--insecure\"}\n\n\taddr := dock.RunAndWait(t, conf, \"26257\", func(addr string) bool {\n\t\thost, portStr, err := net.SplitHostPort(addr)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tport, err := strconv.Atoi(portStr)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tcconf, err := pgx.ParseConfig(\"\")\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tcconf.Host = host\n\t\tcconf.Port = uint16(port)\n\t\tcconf.User = \"root\"\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\tconn, err := pgx.ConnectConfig(ctx, cconf)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tconn.Close(ctx)\n\t\treturn true\n\t})\n\taddr = `postgresql://root@` + addr\n\tdb, err := sql.Open(\"pgx\", addr+`?sslmode=disable`)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer db.Close()\n\tconst dbName = \"cayley\"\n\tif _, err = db.Exec(\"CREATE DATABASE \" + dbName); err != nil {\n\t\tt.Fatal(err)\n\t}\n\taddr = addr + `/` + dbName + `?sslmode=disable`\n\treturn addr, nil\n}\n\nvar conf = &sqltest.Config{\n\tTimeRound: true,\n\tTimeInMcs: true,\n}\n\nfunc TestCockroach(t *testing.T) {\n\tsqltest.TestAll(t, Type, makeCockroach, conf)\n}\n\nfunc BenchmarkCockroach(t *testing.B) {\n\tsqltest.BenchmarkAll(t, Type, makeCockroach, conf)\n}\n"
  },
  {
    "path": "graph/sql/database.go",
    "content": "package sql\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n)\n\nvar types = make(map[string]Registration)\n\nfunc Register(name string, f Registration) {\n\tif f.Driver == \"\" {\n\t\tpanic(\"no sql driver in type definition\")\n\t}\n\ttypes[name] = f\n\n\tregisterQuadStore(name, name)\n}\n\ntype Registration struct {\n\tDriver             string // sql driver to use on dial\n\tHashType           string // type for hash fields\n\tBytesType          string // type for binary fields\n\tTimeType           string // type for datetime fields\n\tHorizonType        string // type for horizon counter\n\tNodesTableExtra    string // extra SQL to append to nodes table definition\n\tConditionalIndexes bool   // database supports conditional indexes\n\tFillFactor         bool   // database supports fill percent on indexes\n\tNoForeignKeys      bool   // database has no support for FKs\n\tCustomNullTime     bool   // driver doesn't support sql.NullTime\n\n\tQueryDialect\n\tNoOffsetWithoutLimit bool // SELECT ... OFFSET can be used only with LIMIT\n\n\tError               func(error) error         // error conversion function\n\tEstimated           func(table string) string // query that string that returns an estimated number of rows in table\n\tRunTx               func(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error\n\tTxRetry             func(tx *sql.Tx, stmts func() error) error\n\tNoSchemaChangesInTx bool\n}\n\nfunc (r Registration) nodesTable() string {\n\thtyp := r.HashType\n\tif htyp == \"\" {\n\t\thtyp = \"BYTEA\"\n\t}\n\tbtyp := r.BytesType\n\tif btyp == \"\" {\n\t\tbtyp = \"BYTEA\"\n\t}\n\tttyp := r.TimeType\n\tif ttyp == \"\" {\n\t\tttyp = \"timestamp with time zone\"\n\t}\n\tend := \"\\n);\"\n\tif r.NodesTableExtra != \"\" {\n\t\tend = \",\\n\" + r.NodesTableExtra + end\n\t}\n\treturn `CREATE TABLE nodes (\n\thash ` + htyp + ` PRIMARY KEY,\n\trefs INT NOT NULL,\n\tvalue ` + btyp + `,\n\tvalue_string TEXT,\n\tdatatype TEXT,\n\tlanguage TEXT,\n\tiri BOOLEAN,\n\tbnode BOOLEAN,\n\tvalue_int BIGINT,\n\tvalue_bool BOOLEAN,\n\tvalue_float double precision,\n\tvalue_time ` + ttyp +\n\t\tend\n}\n\nfunc (r Registration) quadsTable() string {\n\thtyp := r.HashType\n\tif htyp == \"\" {\n\t\thtyp = \"BYTEA\"\n\t}\n\thztyp := r.HorizonType\n\tif hztyp == \"\" {\n\t\thztyp = \"SERIAL\"\n\t}\n\treturn `CREATE TABLE quads (\n\thorizon ` + hztyp + ` PRIMARY KEY,\n\tsubject_hash ` + htyp + ` NOT NULL,\n\tpredicate_hash ` + htyp + ` NOT NULL,\n\tobject_hash ` + htyp + ` NOT NULL,\n\tlabel_hash ` + htyp + `,\n\tts timestamp\n);`\n}\n\nfunc (r Registration) quadIndexes(options graph.Options) []string {\n\tindexes := make([]string, 0, 10)\n\tif r.ConditionalIndexes {\n\t\tindexes = append(indexes,\n\t\t\t`CREATE UNIQUE INDEX spo_unique ON quads (subject_hash, predicate_hash, object_hash) WHERE label_hash IS NULL;`,\n\t\t\t`CREATE UNIQUE INDEX spol_unique ON quads (subject_hash, predicate_hash, object_hash, label_hash) WHERE label_hash IS NOT NULL;`,\n\t\t)\n\t} else {\n\t\tindexes = append(indexes,\n\t\t\t`CREATE UNIQUE INDEX spo_unique ON quads (subject_hash, predicate_hash, object_hash);`,\n\t\t\t`CREATE UNIQUE INDEX spol_unique ON quads (subject_hash, predicate_hash, object_hash, label_hash);`,\n\t\t)\n\t}\n\tif !r.NoForeignKeys {\n\t\tindexes = append(indexes,\n\t\t\t`ALTER TABLE quads ADD CONSTRAINT subject_hash_fk FOREIGN KEY (subject_hash) REFERENCES nodes (hash);`,\n\t\t\t`ALTER TABLE quads ADD CONSTRAINT predicate_hash_fk FOREIGN KEY (predicate_hash) REFERENCES nodes (hash);`,\n\t\t\t`ALTER TABLE quads ADD CONSTRAINT object_hash_fk FOREIGN KEY (object_hash) REFERENCES nodes (hash);`,\n\t\t\t`ALTER TABLE quads ADD CONSTRAINT label_hash_fk FOREIGN KEY (label_hash) REFERENCES nodes (hash);`,\n\t\t)\n\t}\n\tquadIndexes := [][3]quad.Direction{\n\t\t{quad.Subject, quad.Predicate, quad.Object},\n\t\t{quad.Object, quad.Predicate, quad.Subject},\n\t\t{quad.Predicate, quad.Object, quad.Subject},\n\t\t{quad.Object, quad.Subject, quad.Predicate},\n\t}\n\tfactor, _ := options.IntKey(\"db_fill_factor\", 50)\n\tfor _, ind := range quadIndexes {\n\t\tvar (\n\t\t\tname string\n\t\t\tcols []string\n\t\t)\n\t\tfor _, d := range ind {\n\t\t\tname += string(d.Prefix())\n\t\t\tcols = append(cols, d.String()+\"_hash\")\n\t\t}\n\t\tq := fmt.Sprintf(`CREATE INDEX %s_index ON quads (%s)`,\n\t\t\tname, strings.Join(cols, \", \"))\n\t\tif r.FillFactor {\n\t\t\tq += fmt.Sprintf(\" WITH (FILLFACTOR = %d)\", factor)\n\t\t}\n\t\tindexes = append(indexes, q+\";\")\n\t}\n\treturn indexes\n}\n"
  },
  {
    "path": "graph/sql/iterator.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar _ shape.Optimizer = (*QuadStore)(nil)\n\nfunc (qs *QuadStore) OptimizeShape(ctx context.Context, s shape.Shape) (shape.Shape, bool) {\n\treturn qs.opt.OptimizeShape(ctx, s)\n}\n\nfunc (qs *QuadStore) prepareQuery(s Shape) (string, []interface{}) {\n\targs := s.Args()\n\tvals := make([]interface{}, 0, len(args))\n\tfor _, a := range args {\n\t\tvals = append(vals, a.SQLValue())\n\t}\n\tb := NewBuilder(qs.flavor.QueryDialect)\n\tqu := s.SQL(b)\n\treturn qu, vals\n}\n\nfunc (qs *QuadStore) QueryRow(ctx context.Context, s Shape) *sql.Row {\n\tqu, vals := qs.prepareQuery(s)\n\treturn qs.db.QueryRowContext(ctx, qu, vals...)\n}\n\nfunc (qs *QuadStore) Query(ctx context.Context, s Shape) (*sql.Rows, error) {\n\tqu, vals := qs.prepareQuery(s)\n\trows, err := qs.db.QueryContext(ctx, qu, vals...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"sql query failed: %v\\nquery: %v\", err, qu)\n\t}\n\treturn rows, nil\n}\n\nfunc (qs *QuadStore) newIterator(s Select) *Iterator {\n\treturn &Iterator{\n\t\tqs:    qs,\n\t\tquery: s,\n\t}\n}\n\ntype Iterator struct {\n\tqs    *QuadStore\n\tquery Select\n\terr   error\n}\n\nfunc (it *Iterator) Iterate() iterator.Scanner {\n\treturn it.qs.newIteratorNext(it.query)\n}\n\nfunc (it *Iterator) Lookup() iterator.Index {\n\treturn it.qs.newIteratorContains(it.query)\n}\n\nfunc (it *Iterator) Stats(ctx context.Context) (iterator.Costs, error) {\n\tsz, err := it.getSize(ctx)\n\treturn iterator.Costs{\n\t\tNextCost:     1,\n\t\tContainsCost: 10,\n\t\tSize:         sz,\n\t}, err\n}\n\nfunc (it *Iterator) estimateSize(ctx context.Context) int64 {\n\tif it.query.Limit > 0 {\n\t\treturn it.query.Limit\n\t}\n\tst, err := it.qs.Stats(ctx, false)\n\tif err != nil && it.err == nil {\n\t\tit.err = err\n\t}\n\treturn st.Quads.Value\n}\n\nfunc (it *Iterator) getSize(ctx context.Context) (refs.Size, error) {\n\tsz, err := it.qs.querySize(ctx, it.query)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn refs.Size{Value: it.estimateSize(ctx), Exact: false}, err\n\t}\n\treturn sz, nil\n}\n\nfunc (it *Iterator) Optimize(ctx context.Context) (iterator.Shape, bool) {\n\treturn it, false\n}\n\nfunc (it *Iterator) SubIterators() []iterator.Shape {\n\treturn nil\n}\n\nfunc (it *Iterator) String() string {\n\treturn it.query.SQL(NewBuilder(it.qs.flavor.QueryDialect))\n}\n\nfunc newIteratorBase(qs *QuadStore, s Select) iteratorBase {\n\treturn iteratorBase{\n\t\tqs:    qs,\n\t\tquery: s,\n\t}\n}\n\ntype iteratorBase struct {\n\tqs    *QuadStore\n\tquery Select\n\n\tcols []string\n\tcind map[quad.Direction]int\n\n\terr  error\n\tres  graph.Ref\n\ttags map[string]graph.Ref\n}\n\nfunc (it *iteratorBase) TagResults(m map[string]graph.Ref) {\n\tfor tag, val := range it.tags {\n\t\tm[tag] = val\n\t}\n}\n\nfunc (it *iteratorBase) Result() graph.Ref {\n\treturn it.res\n}\n\nfunc (it *iteratorBase) ensureColumns() {\n\tif it.cols != nil {\n\t\treturn\n\t}\n\tit.cols = it.query.Columns()\n\tit.cind = make(map[quad.Direction]int, len(quad.Directions)+1)\n\tfor i, name := range it.cols {\n\t\tif !strings.HasPrefix(name, tagPref) {\n\t\t\tcontinue\n\t\t}\n\t\tif name == tagNode {\n\t\t\tit.cind[quad.Any] = i\n\t\t\tcontinue\n\t\t}\n\t\tname = name[len(tagPref):]\n\t\tfor _, d := range quad.Directions {\n\t\t\tif name == d.String() {\n\t\t\t\tit.cind[d] = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (it *iteratorBase) scanValue(r *sql.Rows) bool {\n\tit.ensureColumns()\n\tnodes := make([]NodeHash, len(it.cols))\n\tpointers := make([]interface{}, len(nodes))\n\tfor i := range pointers {\n\t\tpointers[i] = &nodes[i]\n\t}\n\tif err := r.Scan(pointers...); err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tit.tags = make(map[string]graph.Ref)\n\tfor i, name := range it.cols {\n\t\tif !strings.Contains(name, tagPref) {\n\t\t\tit.tags[name] = nodes[i].ValueHash\n\t\t}\n\t}\n\tif len(it.cind) > 1 {\n\t\tvar q QuadHashes\n\t\tfor _, d := range quad.Directions {\n\t\t\ti, ok := it.cind[d]\n\t\t\tif !ok {\n\t\t\t\tit.err = fmt.Errorf(\"cannot find quad %v in query output (columns: %v)\", d, it.cols)\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tq.Set(d, nodes[i].ValueHash)\n\t\t}\n\t\tit.res = q\n\t\treturn true\n\t}\n\ti, ok := it.cind[quad.Any]\n\tif !ok {\n\t\tit.err = fmt.Errorf(\"cannot find node hash in query output (columns: %v, cind: %v)\", it.cols, it.cind)\n\t\treturn false\n\t}\n\tit.res = nodes[i]\n\treturn true\n}\n\nfunc (it *iteratorBase) Err() error {\n\treturn it.err\n}\n\nfunc (it *iteratorBase) String() string {\n\treturn it.query.SQL(NewBuilder(it.qs.flavor.QueryDialect))\n}\n\nfunc (qs *QuadStore) newIteratorNext(s Select) *iteratorNext {\n\treturn &iteratorNext{\n\t\titeratorBase: newIteratorBase(qs, s),\n\t}\n}\n\ntype iteratorNext struct {\n\titeratorBase\n\tcursor *sql.Rows\n\t// TODO(dennwc): nextPath workaround; remove when we get rid of NextPath in general\n\tnextPathRes  graph.Ref\n\tnextPathTags map[string]graph.Ref\n}\n\nfunc (it *iteratorNext) Next(ctx context.Context) bool {\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif it.cursor == nil {\n\t\tit.cursor, it.err = it.qs.Query(ctx, it.query)\n\t}\n\t// TODO(dennwc): this loop exists only because of nextPath workaround\n\tfor {\n\t\tif it.err != nil {\n\t\t\treturn false\n\t\t}\n\t\tif it.nextPathRes != nil {\n\t\t\tit.res = it.nextPathRes\n\t\t\tit.tags = it.nextPathTags\n\t\t\tit.nextPathRes = nil\n\t\t\tit.nextPathTags = nil\n\t\t\treturn true\n\t\t}\n\t\tif !it.cursor.Next() {\n\t\t\tit.err = it.cursor.Err()\n\t\t\tit.cursor.Close()\n\t\t\treturn false\n\t\t}\n\n\t\tprev := it.res\n\t\tif !it.scanValue(it.cursor) {\n\t\t\treturn false\n\t\t}\n\t\tif !it.query.nextPath {\n\t\t\treturn true\n\t\t}\n\t\tif prev == nil || prev.Key() != it.res.Key() {\n\t\t\treturn true\n\t\t}\n\t\t// skip the same main key if in nextPath mode\n\t\t// the user should receive accept those results via NextPath of the iterator\n\t}\n}\n\nfunc (it *iteratorNext) NextPath(ctx context.Context) bool {\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif !it.query.nextPath {\n\t\treturn false\n\t}\n\tif !it.cursor.Next() {\n\t\tit.err = it.cursor.Err()\n\t\tit.cursor.Close()\n\t\treturn false\n\t}\n\tprev := it.res\n\tif !it.scanValue(it.cursor) {\n\t\treturn false\n\t}\n\tif prev.Key() == it.res.Key() {\n\t\treturn true\n\t}\n\t// different main keys - return false, but keep this results for the Next\n\tit.nextPathRes = it.res\n\tit.nextPathTags = it.tags\n\tit.res = nil\n\tit.tags = nil\n\treturn false\n}\n\nfunc (it *iteratorNext) Close() error {\n\tif it.cursor != nil {\n\t\tit.cursor.Close()\n\t\tit.cursor = nil\n\t}\n\treturn nil\n}\n\nfunc (qs *QuadStore) newIteratorContains(s Select) *iteratorContains {\n\treturn &iteratorContains{\n\t\titeratorBase: newIteratorBase(qs, s),\n\t}\n}\n\ntype iteratorContains struct {\n\titeratorBase\n\t// TODO(dennwc): nextPath workaround; remove when we get rid of NextPath in general\n\tnextPathRows *sql.Rows\n}\n\nfunc (it *iteratorContains) Contains(ctx context.Context, v graph.Ref) bool {\n\tit.ensureColumns()\n\tsel := it.query\n\tsel.Where = append([]Where{}, sel.Where...)\n\tswitch v := v.(type) {\n\tcase NodeHash:\n\t\ti, ok := it.cind[quad.Any]\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tf := it.query.Fields[i]\n\t\tsel.WhereEq(f.Table, f.Name, v)\n\tcase QuadHashes:\n\t\tfor _, d := range quad.Directions {\n\t\t\ti, ok := it.cind[d]\n\t\t\tif !ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\th := v.Get(d)\n\t\t\tif !h.Valid() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf := it.query.Fields[i]\n\t\t\tsel.WhereEq(f.Table, f.Name, NodeHash{h})\n\t\t}\n\tdefault:\n\t\treturn false\n\t}\n\n\trows, err := it.qs.Query(ctx, sel)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn false\n\t}\n\tif it.query.nextPath {\n\t\tif it.nextPathRows != nil {\n\t\t\t_ = it.nextPathRows.Close()\n\t\t}\n\t\tit.nextPathRows = rows\n\t} else {\n\t\tdefer rows.Close()\n\t}\n\tif !rows.Next() {\n\t\tit.err = rows.Err()\n\t\treturn false\n\t}\n\treturn it.scanValue(rows)\n}\n\nfunc (it *iteratorContains) NextPath(ctx context.Context) bool {\n\tif it.err != nil {\n\t\treturn false\n\t}\n\tif !it.query.nextPath {\n\t\treturn false\n\t}\n\tif !it.nextPathRows.Next() {\n\t\tit.err = it.nextPathRows.Err()\n\t\treturn false\n\t}\n\treturn it.scanValue(it.nextPathRows)\n}\n\nfunc (it *iteratorContains) Close() error {\n\tif it.nextPathRows != nil {\n\t\treturn it.nextPathRows.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "graph/sql/mysql/mysql.go",
    "content": "package mysql\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/go-sql-driver/mysql\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n\tcsql \"github.com/cayleygraph/cayley/graph/sql\"\n)\n\nconst Type = \"mysql\"\n\nvar QueryDialect = csql.QueryDialect{\n\tRegexpOp: \"REGEXP\",\n\tFieldQuote: func(name string) string {\n\t\treturn \"`\" + name + \"`\"\n\t},\n\tPlaceholder: func(n int) string { return \"?\" },\n}\n\nfunc init() {\n\tcsql.Register(Type, csql.Registration{\n\t\tDriver:               \"mysql\",\n\t\tHashType:             fmt.Sprintf(`BINARY(%d)`, quad.HashSize),\n\t\tBytesType:            `BLOB`,\n\t\tHorizonType:          `SERIAL`,\n\t\tTimeType:             `DATETIME(6)`,\n\t\tQueryDialect:         QueryDialect,\n\t\tNoOffsetWithoutLimit: true,\n\t\tCustomNullTime:       true,\n\t\tError: func(err error) error {\n\t\t\treturn err\n\t\t},\n\t\tEstimated: nil,\n\t\tRunTx:     runTxMysql,\n\t})\n}\n\nfunc runTxMysql(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error {\n\t// update node ref counts and insert nodes\n\tvar (\n\t\t// prepared statements for each value type\n\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\tupdateValue *sql.Stmt\n\t)\n\tfor _, n := range nodes {\n\t\tif n.RefInc >= 0 {\n\t\t\tnodeKey, values, err := csql.NodeValues(csql.NodeHash{ValueHash: n.Hash}, n.Val)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvalues = append([]interface{}{n.RefInc}, values...)\n\t\t\tvalues = append(values, n.RefInc) // one more time for UPDATE\n\t\t\tstmt, ok := insertValue[nodeKey]\n\t\t\tif !ok {\n\t\t\t\tvar ph = make([]string, len(values)-1) // excluding last increment\n\t\t\t\tfor i := range ph {\n\t\t\t\t\tph[i] = \"?\"\n\t\t\t\t}\n\t\t\t\tstmt, err = tx.Prepare(`INSERT INTO nodes(refs, hash, ` +\n\t\t\t\t\tstrings.Join(nodeKey.Columns(), \", \") +\n\t\t\t\t\t`) VALUES (` + strings.Join(ph, \", \") +\n\t\t\t\t\t`) ON DUPLICATE KEY UPDATE refs = refs + ?;`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue[nodeKey] = stmt\n\t\t\t}\n\t\t\t_, err = stmt.Exec(values...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected node update\")\n\t\t}\n\t}\n\tfor _, s := range insertValue {\n\t\ts.Close()\n\t}\n\tif s := updateValue; s != nil {\n\t\ts.Close()\n\t}\n\tinsertValue = nil\n\tupdateValue = nil\n\n\t// now we can deal with quads\n\tignore := \"\"\n\tif opts.IgnoreDup {\n\t\tignore = \" IGNORE\"\n\t}\n\n\tvar (\n\t\tinsertQuad *sql.Stmt\n\t\terr        error\n\t)\n\tfor _, d := range quads {\n\t\tdirs := make([]interface{}, 0, len(quad.Directions))\n\t\tfor _, h := range d.Quad.Dirs() {\n\t\t\tdirs = append(dirs, csql.NodeHash{ValueHash: h}.SQLValue())\n\t\t}\n\t\tif !d.Del {\n\t\t\tif insertQuad == nil {\n\t\t\t\tinsertQuad, err = tx.Prepare(`INSERT` + ignore + ` INTO quads(subject_hash, predicate_hash, object_hash, label_hash, ts) VALUES (?, ?, ?, ?, now());`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\t\t}\n\t\t\t_, err := insertQuad.Exec(dirs...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tif _, ok := err.(*graph.DeltaError); !ok {\n\t\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected quad delete\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc convInsertError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif e, ok := err.(*mysql.MySQLError); ok {\n\t\tif e.Number == 1062 {\n\t\t\t// TODO: reference to delta\n\t\t\treturn &graph.DeltaError{Err: graph.ErrQuadExists}\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "graph/sql/mysql/mysql_test.go",
    "content": "//go:build docker\n// +build docker\n\npackage mysql\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/sql/sqltest\"\n\t\"github.com/cayleygraph/cayley/internal/dock\"\n)\n\nfunc makeMysqlVersion(image string) sqltest.DatabaseFunc {\n\treturn func(t testing.TB) (string, graph.Options) {\n\t\tvar conf dock.Config\n\n\t\tconf.Image = image\n\t\tconf.Tty = true\n\t\tconf.Env = []string{\n\t\t\t`MYSQL_ROOT_PASSWORD=root`,\n\t\t\t`MYSQL_DATABASE=testdb`,\n\t\t}\n\n\t\taddr := dock.RunAndWait(t, conf, \"3306\", nil)\n\t\taddr = `root:root@tcp(` + addr + `)/testdb`\n\t\treturn addr, nil\n\t}\n}\n\nconst (\n\tmysqlImage   = \"mysql:8\"\n\tmariadbImage = \"mariadb:11\"\n)\n\nfunc TestMysql(t *testing.T) {\n\tsqltest.TestAll(t, Type, makeMysqlVersion(mysqlImage), nil)\n}\n\nfunc TestMariaDB(t *testing.T) {\n\tsqltest.TestAll(t, Type, makeMysqlVersion(mariadbImage), nil)\n}\n\nfunc BenchmarkMysql(t *testing.B) {\n\tsqltest.BenchmarkAll(t, Type, makeMysqlVersion(mysqlImage), nil)\n}\n\nfunc BenchmarkMariadb(t *testing.B) {\n\tsqltest.BenchmarkAll(t, Type, makeMysqlVersion(mariadbImage), nil)\n}\n"
  },
  {
    "path": "graph/sql/optimizer.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc NewOptimizer() *Optimizer {\n\treturn &Optimizer{}\n}\n\ntype Optimizer struct {\n\ttableInd int\n\n\tregexpOp             CmpOp\n\tnoOffsetWithoutLimit bool // blame mysql\n}\n\nfunc (opt *Optimizer) SetRegexpOp(op CmpOp) {\n\topt.regexpOp = op\n}\n\nfunc (opt *Optimizer) NoOffsetWithoutLimit() {\n\topt.noOffsetWithoutLimit = true\n}\n\nfunc (opt *Optimizer) nextTable() string {\n\topt.tableInd++\n\treturn fmt.Sprintf(\"t_%d\", opt.tableInd)\n}\n\nfunc (opt *Optimizer) ensureAliases(s *Select) {\n\tfor i, src := range s.From {\n\t\tif t, ok := src.(Table); ok && t.Alias == \"\" {\n\t\t\tt.Alias = opt.nextTable()\n\t\t\ts.From[i] = t\n\t\t\t// TODO: copy slice\n\t\t\tfor j := range s.Fields {\n\t\t\t\tf := &s.Fields[j]\n\t\t\t\tif f.Table == \"\" {\n\t\t\t\t\tf.Table = t.Alias\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor j := range s.Where {\n\t\t\t\tw := &s.Where[j]\n\t\t\t\tif w.Table == \"\" {\n\t\t\t\t\tw.Table = t.Alias\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sortDirs(dirs []quad.Direction) {\n\tsort.Slice(dirs, func(i, j int) bool {\n\t\treturn dirs[i] < dirs[j]\n\t})\n}\n\nfunc (opt *Optimizer) OptimizeShape(ctx context.Context, s shape.Shape) (shape.Shape, bool) {\n\tswitch s := s.(type) {\n\tcase shape.AllNodes:\n\t\treturn AllNodes(), true\n\tcase shape.Lookup:\n\t\treturn opt.optimizeLookup(s)\n\tcase shape.Filter:\n\t\treturn opt.optimizeFilters(s)\n\tcase shape.Intersect:\n\t\treturn opt.optimizeIntersect(s)\n\tcase shape.Quads:\n\t\treturn opt.optimizeQuads(s)\n\tcase shape.NodesFrom:\n\t\treturn opt.optimizeNodesFrom(s)\n\tcase shape.QuadsAction:\n\t\treturn opt.optimizeQuadsAction(s)\n\tcase shape.Save:\n\t\treturn opt.optimizeSave(s)\n\tcase shape.Page:\n\t\treturn opt.optimizePage(s)\n\tdefault:\n\t\treturn s, false\n\t}\n}\n\nfunc selectValueQuery(v quad.Value, op CmpOp) ([]Where, []Value, bool) {\n\tif op == OpEqual {\n\t\t// we can use hash to check equality\n\t\treturn []Where{\n\t\t\t\t{Field: \"hash\", Op: op, Value: Placeholder{}},\n\t\t\t}, []Value{\n\t\t\t\tHashOf(v),\n\t\t\t}, true\n\t}\n\tvar (\n\t\twhere  []Where\n\t\tparams []Value\n\t)\n\tswitch v := v.(type) {\n\tcase quad.IRI:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_string\", Op: op, Value: Placeholder{}},\n\t\t\t{Field: \"iri\", Op: OpIsTrue},\n\t\t}\n\t\tparams = []Value{\n\t\t\tStringVal(v),\n\t\t}\n\tcase quad.BNode:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_string\", Op: op, Value: Placeholder{}},\n\t\t\t{Field: \"bnode\", Op: OpIsTrue},\n\t\t}\n\t\tparams = []Value{\n\t\t\tStringVal(v),\n\t\t}\n\tcase quad.String:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_string\", Op: op, Value: Placeholder{}},\n\t\t\t{Field: \"iri\", Op: OpIsNull},\n\t\t\t{Field: \"bnode\", Op: OpIsNull},\n\t\t\t{Field: \"datatype\", Op: OpIsNull},\n\t\t\t{Field: \"language\", Op: OpIsNull},\n\t\t}\n\t\tparams = []Value{\n\t\t\tStringVal(v),\n\t\t}\n\tcase quad.LangString:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_string\", Op: op, Value: Placeholder{}},\n\t\t\t{Field: \"language\", Op: OpEqual, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tStringVal(v.Value),\n\t\t\tStringVal(v.Lang),\n\t\t}\n\tcase quad.TypedString:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_string\", Op: op, Value: Placeholder{}},\n\t\t\t{Field: \"datatype\", Op: OpEqual, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tStringVal(v.Value),\n\t\t\tStringVal(v.Type),\n\t\t}\n\tcase quad.Int:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_int\", Op: op, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tIntVal(v),\n\t\t}\n\tcase quad.Float:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_float\", Op: op, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tFloatVal(v),\n\t\t}\n\tcase quad.Bool:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_bool\", Op: op, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tBoolVal(v),\n\t\t}\n\tcase quad.Time:\n\t\twhere = []Where{\n\t\t\t{Field: \"value_time\", Op: op, Value: Placeholder{}},\n\t\t}\n\t\tparams = []Value{\n\t\t\tTimeVal(v),\n\t\t}\n\tdefault:\n\t\treturn nil, nil, false\n\t}\n\treturn where, params, true\n}\n\nfunc SelectValue(v quad.Value, op CmpOp) *Select {\n\twhere, params, ok := selectValueQuery(v, op)\n\tif !ok {\n\t\treturn nil\n\t}\n\tsel := Nodes(where, params)\n\treturn &sel\n}\n\nfunc (opt *Optimizer) optimizeLookup(s shape.Lookup) (shape.Shape, bool) {\n\tif len(s) != 1 {\n\t\t// TODO: support for IN\n\t\treturn s, false\n\t}\n\tsel := SelectValue(s[0], OpEqual)\n\tif sel == nil {\n\t\treturn s, false\n\t}\n\treturn *sel, true\n}\n\nfunc convRegexp(re string) string {\n\treturn re // TODO: convert regular expression\n}\n\nfunc (opt *Optimizer) optimizeFilter(from shape.Shape, f shape.ValueFilter) ([]Where, []Value, bool) {\n\tswitch f := f.(type) {\n\tcase shape.Comparison:\n\t\tvar cmp CmpOp\n\t\tswitch f.Op {\n\t\tcase iterator.CompareGT:\n\t\t\tcmp = OpGT\n\t\tcase iterator.CompareGTE:\n\t\t\tcmp = OpGTE\n\t\tcase iterator.CompareLT:\n\t\t\tcmp = OpLT\n\t\tcase iterator.CompareLTE:\n\t\t\tcmp = OpLTE\n\t\tdefault:\n\t\t\treturn nil, nil, false\n\t\t}\n\t\treturn selectValueQuery(f.Val, cmp)\n\tcase shape.Wildcard:\n\t\tif opt.regexpOp == \"\" {\n\t\t\treturn nil, nil, false\n\t\t}\n\t\treturn []Where{\n\t\t\t\t{Field: \"value_string\", Op: opt.regexpOp, Value: Placeholder{}},\n\t\t\t}, []Value{\n\t\t\t\tStringVal(convRegexp(f.Regexp())),\n\t\t\t}, true\n\tcase shape.Regexp:\n\t\tif opt.regexpOp == \"\" {\n\t\t\treturn nil, nil, false\n\t\t}\n\t\twhere := []Where{\n\t\t\t{Field: \"value_string\", Op: opt.regexpOp, Value: Placeholder{}},\n\t\t}\n\t\tif !f.Refs {\n\t\t\twhere = append(where, []Where{\n\t\t\t\t{Field: \"iri\", Op: OpIsNull},\n\t\t\t\t{Field: \"bnode\", Op: OpIsNull},\n\t\t\t}...)\n\t\t}\n\t\treturn where, []Value{\n\t\t\tStringVal(convRegexp(f.Re.String())),\n\t\t}, true\n\tdefault:\n\t\treturn nil, nil, false\n\t}\n}\nfunc (opt *Optimizer) optimizeFilters(s shape.Filter) (shape.Shape, bool) {\n\tswitch from := s.From.(type) {\n\tcase shape.AllNodes:\n\tcase Select:\n\t\tif !from.isAll() {\n\t\t\treturn s, false\n\t\t}\n\t\tt, ok := from.From[0].(Table)\n\t\tif !ok || t.Name != \"nodes\" {\n\t\t\treturn s, false\n\t\t}\n\tdefault:\n\t\treturn s, false\n\t}\n\tvar (\n\t\twhere  []Where\n\t\tparams []Value\n\t)\n\tleft := shape.Filter{\n\t\tFrom: s.From,\n\t}\n\tfor _, f := range s.Filters {\n\t\tif w, p, ok := opt.optimizeFilter(s.From, f); ok {\n\t\t\twhere = append(where, w...)\n\t\t\tparams = append(params, p...)\n\t\t} else {\n\t\t\tleft.Filters = append(left.Filters, f)\n\t\t}\n\t}\n\tif len(where) == 0 {\n\t\treturn s, false\n\t}\n\tsel := Nodes(where, params)\n\tif len(left.Filters) == 0 {\n\t\treturn sel, true\n\t}\n\tleft.From = sel\n\treturn left, true\n}\n\nfunc (opt *Optimizer) optimizeQuads(s shape.Quads) (shape.Shape, bool) {\n\tt1 := opt.nextTable()\n\tsel := AllQuads(t1)\n\tfor _, f := range s {\n\t\twr := Where{\n\t\t\tTable: t1,\n\t\t\tField: dirField(f.Dir),\n\t\t\tOp:    OpEqual,\n\t\t}\n\t\tswitch fv := f.Values.(type) {\n\t\tcase shape.Fixed:\n\t\t\tif len(fv) != 1 {\n\t\t\t\t// TODO: support IN, or generate SELECT equivalent\n\t\t\t\treturn s, false\n\t\t\t}\n\t\t\twr.Value = sel.AppendParam(fv[0].(Value))\n\t\t\tsel.Where = append(sel.Where, wr)\n\t\tcase Select:\n\t\t\tif len(fv.Fields) == 1 {\n\t\t\t\t// simple case - just add subquery to FROM\n\t\t\t\ttbl := opt.nextTable()\n\t\t\t\tsel.From = append(sel.From, Subquery{\n\t\t\t\t\tQuery: fv,\n\t\t\t\t\tAlias: tbl,\n\t\t\t\t})\n\t\t\t\twr.Value = FieldName{\n\t\t\t\t\tName:  fv.Fields[0].NameOrAlias(),\n\t\t\t\t\tTable: tbl,\n\t\t\t\t}\n\t\t\t\tsel.Where = append(sel.Where, wr)\n\t\t\t\tcontinue\n\t\t\t} else if fv.onlyAsSubquery() {\n\t\t\t\t// TODO: generic subquery: pass all tags to main query, set WHERE on specific direction, drop __* tags\n\t\t\t\treturn s, false\n\t\t\t}\n\t\t\topt.ensureAliases(&fv)\n\t\t\t// add all tables from subquery to the main one, but skip __node field - we should add it to WHERE\n\t\t\tvar head Field\n\t\t\tfor _, f := range fv.Fields {\n\t\t\t\tif f.Alias == tagNode {\n\t\t\t\t\tfor _, w := range fv.Where {\n\t\t\t\t\t\tif w.Table == f.Table && w.Field == f.Alias {\n\t\t\t\t\t\t\t// TODO: if __node was used in WHERE of subquery, we should rewrite it\n\t\t\t\t\t\t\treturn s, false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tf.Alias = \"\"\n\t\t\t\t\thead = f\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsel.Fields = append(sel.Fields, f)\n\t\t\t}\n\t\t\tif head.Table == \"\" {\n\t\t\t\t// something is wrong\n\t\t\t\treturn s, false\n\t\t\t}\n\t\t\tsel.From = append(sel.From, fv.From...)\n\t\t\tsel.Where = append(sel.Where, fv.Where...)\n\t\t\tsel.Params = append(sel.Params, fv.Params...)\n\t\t\twr.Value = FieldName{\n\t\t\t\tName:  head.Name,\n\t\t\t\tTable: head.Table,\n\t\t\t}\n\t\t\tsel.Where = append(sel.Where, wr)\n\t\tdefault:\n\t\t\treturn s, false\n\t\t}\n\t}\n\treturn sel, true\n}\n\nfunc (opt *Optimizer) optimizeNodesFrom(s shape.NodesFrom) (shape.Shape, bool) {\n\tsel, ok := s.Quads.(Select)\n\tif !ok {\n\t\treturn s, false\n\t}\n\tsel.Fields = append([]Field{}, sel.Fields...)\n\n\t// all we need is to remove all quad-related tags and preserve one with matching direction\n\tdir := dirTag(s.Dir)\n\tfound := false\n\tfor i := 0; i < len(sel.Fields); i++ {\n\t\tf := &sel.Fields[i]\n\t\tif f.Alias == dir {\n\t\t\tf.Alias = tagNode\n\t\t\tfound = true\n\t\t} else if strings.HasPrefix(f.Alias, tagPref) {\n\t\t\tsel.Fields = append(sel.Fields[:i], sel.Fields[i+1:]...)\n\t\t\ti--\n\t\t}\n\t}\n\tif !found {\n\t\treturn s, false\n\t}\n\t// NodesFrom implies that the iterator will use NextPath\n\tsel.nextPath = true\n\treturn sel, true\n}\n\nfunc (opt *Optimizer) optimizeQuadsAction(s shape.QuadsAction) (shape.Shape, bool) {\n\tsel := Select{\n\t\tFields: []Field{\n\t\t\t{Name: dirField(s.Result), Alias: tagNode},\n\t\t},\n\t\tFrom: []Source{\n\t\t\tTable{Name: \"quads\"},\n\t\t},\n\t\t// NodesFrom (that is a part of QuadsAction) implies that the iterator will use NextPath\n\t\tnextPath: true,\n\t}\n\tvar dirs []quad.Direction\n\tfor d := range s.Save {\n\t\tdirs = append(dirs, d)\n\t}\n\tsortDirs(dirs)\n\tfor _, d := range dirs {\n\t\tfor _, t := range s.Save[d] {\n\t\t\tsel.Fields = append(sel.Fields, Field{\n\t\t\t\tName: dirField(d), Alias: t,\n\t\t\t})\n\t\t}\n\t}\n\tdirs = nil\n\tfor d := range s.Filter {\n\t\tdirs = append(dirs, d)\n\t}\n\tsortDirs(dirs)\n\tfor _, d := range dirs {\n\t\tv := s.Filter[d]\n\t\tsel.WhereEq(\"\", dirField(d), v.(Value))\n\t}\n\treturn sel, true\n}\n\nfunc (opt *Optimizer) optimizeSave(s shape.Save) (shape.Shape, bool) {\n\tsel, ok := s.From.(Select)\n\tif !ok {\n\t\treturn s, false\n\t}\n\t// find primary value used by iterators\n\tfi := -1\n\tfor i, f := range sel.Fields {\n\t\tif f.Alias == tagNode {\n\t\t\tfi = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif fi < 0 {\n\t\treturn s, false\n\t}\n\t// add SELECT fields as aliases for primary field\n\tf := sel.Fields[fi]\n\tfields := make([]Field, 0, len(s.Tags)+len(sel.Fields))\n\tfor _, tag := range s.Tags {\n\t\tf.Alias = tag\n\t\tfields = append(fields, f)\n\t}\n\t// add other fields\n\tfields = append(fields, sel.Fields...)\n\tsel.Fields = fields\n\treturn sel, true\n}\n\nfunc (opt *Optimizer) optimizePage(s shape.Page) (shape.Shape, bool) {\n\tsel, ok := s.From.(Select)\n\tif !ok {\n\t\treturn s, false\n\t}\n\t// do not optimize if db only can use offset with limit, and we have no limits set\n\tif opt.noOffsetWithoutLimit && sel.Limit == 0 && s.Limit == 0 {\n\t\treturn s, false\n\t}\n\t// call shapes optimizer to calculate correct skip and limit\n\tp := shape.Page{\n\t\tSkip:  sel.Offset,\n\t\tLimit: sel.Limit,\n\t}.ApplyPage(s)\n\tif p == nil {\n\t\t// no intersection - no results\n\t\treturn nil, true\n\t}\n\tsel.Limit = p.Limit\n\tsel.Offset = p.Skip\n\treturn sel, true\n}\n\nfunc (opt *Optimizer) optimizeIntersect(s shape.Intersect) (shape.Shape, bool) {\n\tvar (\n\t\tsels  []Select\n\t\tother shape.Intersect\n\t)\n\t// we will add our merged Select to this slot\n\tother = append(other, nil)\n\tfor _, sub := range s {\n\t\t// TODO: sort by onlySubquery flag first\n\t\tif sel, ok := sub.(Select); ok && !sel.onlyAsSubquery() {\n\t\t\tsels = append(sels, sel)\n\t\t} else {\n\t\t\tother = append(other, sub)\n\t\t}\n\t}\n\tif len(sels) <= 1 {\n\t\treturn s, false\n\t}\n\tfor i := range sels {\n\t\tsels[i] = sels[i].Clone()\n\t\topt.ensureAliases(&sels[i])\n\t}\n\tpri := sels[0]\n\tvar head *Field\n\tfor i, f := range pri.Fields {\n\t\tif f.Alias == tagNode {\n\t\t\thead = &pri.Fields[i]\n\t\t\tbreak\n\t\t}\n\t}\n\tif head == nil {\n\t\treturn s, false\n\t}\n\tsec := sels[1:]\n\n\tnextPath := false\n\tfor _, s2 := range sec {\n\t\t// merge From, Where and Params\n\t\tpri.From = append(pri.From, s2.From...)\n\t\tpri.Where = append(pri.Where, s2.Where...)\n\t\tpri.Params = append(pri.Params, s2.Params...)\n\t\tnextPath = nextPath || s2.nextPath\n\t\t// also find and remove primary tag, but add the same field to WHERE\n\t\tok := false\n\t\tfor _, f := range s2.Fields {\n\t\t\tif f.Alias == tagNode {\n\t\t\t\tok = true\n\t\t\t\tpri.Where = append(pri.Where, Where{\n\t\t\t\t\tTable: head.Table,\n\t\t\t\t\tField: head.Name,\n\t\t\t\t\tOp:    OpEqual,\n\t\t\t\t\tValue: FieldName{\n\t\t\t\t\t\tTable: f.Table,\n\t\t\t\t\t\tName:  f.Name,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tpri.Fields = append(pri.Fields, f)\n\t\t\t}\n\t\t}\n\t\tif !ok {\n\t\t\treturn s, false\n\t\t}\n\t}\n\tif len(other) == 1 {\n\t\tpri.nextPath = pri.nextPath || nextPath\n\t\treturn pri, true\n\t}\n\tother[0] = pri\n\treturn other, true\n}\n"
  },
  {
    "path": "graph/sql/postgres/postgres.go",
    "content": "package postgres\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/log\"\n\tcsql \"github.com/cayleygraph/cayley/graph/sql\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/lib/pq\"\n)\n\nconst Type = \"postgres\"\n\nvar QueryDialect = csql.QueryDialect{\n\tRegexpOp:   \"~\",\n\tFieldQuote: pq.QuoteIdentifier,\n\tPlaceholder: func(n int) string {\n\t\treturn fmt.Sprintf(\"$%d\", n)\n\t},\n}\n\nfunc init() {\n\tcsql.Register(Type, csql.Registration{\n\t\tDriver:             \"postgres\",\n\t\tHashType:           `BYTEA`,\n\t\tBytesType:          `BYTEA`,\n\t\tHorizonType:        `BIGSERIAL`,\n\t\tTimeType:           `timestamp with time zone`,\n\t\tQueryDialect:       QueryDialect,\n\t\tConditionalIndexes: true,\n\t\tFillFactor:         true,\n\t\tError:              ConvError,\n\t\tEstimated: func(table string) string {\n\t\t\treturn \"SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname='\" + table + \"';\"\n\t\t},\n\t\tRunTx: RunTxPostgres,\n\t})\n}\n\nfunc ConvError(err error) error {\n\te, ok := err.(*pq.Error)\n\tif !ok {\n\t\treturn err\n\t}\n\tswitch e.Code {\n\tcase \"42P07\":\n\t\treturn graph.ErrDatabaseExists\n\t}\n\treturn err\n}\n\nfunc convInsertError(err error) error {\n\tif err == nil {\n\t\treturn err\n\t}\n\tif pe, ok := err.(*pq.Error); ok {\n\t\tif pe.Code == \"23505\" {\n\t\t\t// TODO: reference to delta\n\t\t\treturn &graph.DeltaError{Err: graph.ErrQuadExists}\n\t\t}\n\t}\n\treturn err\n}\n\n//func copyFromPG(tx *sql.Tx, in []graph.Delta, opts graph.IgnoreOpts) error {\n//\tpanic(\"broken\")\n//\tstmt, err := tx.Prepare(pq.CopyIn(\"quads\", \"subject\", \"predicate\", \"object\", \"label\", \"id\", \"ts\", \"subject_hash\", \"predicate_hash\", \"object_hash\", \"label_hash\"))\n//\tif err != nil {\n//\t\tclog.Errorf(\"couldn't prepare COPY statement: %v\", err)\n//\t\treturn err\n//\t}\n//\tfor _, d := range in {\n//\t\ts, p, o, l, err := marshalQuadDirections(d.Quad)\n//\t\tif err != nil {\n//\t\t\tclog.Errorf(\"couldn't marshal quads: %v\", err)\n//\t\t\treturn err\n//\t\t}\n//\t\t_, err = stmt.Exec(\n//\t\t\ts,\n//\t\t\tp,\n//\t\t\to,\n//\t\t\tl,\n//\t\t\td.ID.Int(),\n//\t\t\td.Timestamp,\n//\t\t\thashOf(d.Quad.Subject),\n//\t\t\thashOf(d.Quad.Predicate),\n//\t\t\thashOf(d.Quad.Object),\n//\t\t\thashOf(d.Quad.Label),\n//\t\t)\n//\t\tif err != nil {\n//\t\t\terr = convInsertErrorPG(err)\n//\t\t\tclog.Errorf(\"couldn't execute COPY statement: %v\", err)\n//\t\t\treturn err\n//\t\t}\n//\t}\n//\t_, err = stmt.Exec()\n//\tif err != nil {\n//\t\terr = convInsertErrorPG(err)\n//\t\treturn err\n//\t}\n//\t_ = stmt.Close() // COPY will be closed on last Exec, this will return non-nil error in all cases\n//\treturn nil\n//}\n\nfunc RunTxPostgres(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error {\n\treturn RunTx(tx, nodes, quads, opts, \"\")\n}\n\nfunc RunTx(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts, onConflict string) error {\n\t// update node ref counts and insert nodes\n\tvar (\n\t\t// prepared statements for each value type\n\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\tupdateValue *sql.Stmt\n\t)\n\tfor _, n := range nodes {\n\t\tif n.RefInc >= 0 {\n\t\t\tnodeKey, values, err := csql.NodeValues(csql.NodeHash{ValueHash: n.Hash}, n.Val)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvalues = append([]interface{}{n.RefInc}, values...)\n\t\t\tstmt, ok := insertValue[nodeKey]\n\t\t\tif !ok {\n\t\t\t\tvar ph = make([]string, len(values))\n\t\t\t\tfor i := range ph {\n\t\t\t\t\tph[i] = \"$\" + strconv.FormatInt(int64(i)+1, 10)\n\t\t\t\t}\n\t\t\t\tstmt, err = tx.Prepare(`INSERT INTO nodes(refs, hash, ` +\n\t\t\t\t\tstrings.Join(nodeKey.Columns(), \", \") +\n\t\t\t\t\t`) VALUES (` + strings.Join(ph, \", \") +\n\t\t\t\t\t`) ON CONFLICT (hash) DO UPDATE SET refs = nodes.refs + EXCLUDED.refs;`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue[nodeKey] = stmt\n\t\t\t}\n\t\t\t_, err = stmt.Exec(values...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected node update\")\n\t\t}\n\t}\n\tfor _, s := range insertValue {\n\t\ts.Close()\n\t}\n\tif s := updateValue; s != nil {\n\t\ts.Close()\n\t}\n\tinsertValue = nil\n\tupdateValue = nil\n\n\t// now we can deal with quads\n\n\t// TODO: copy\n\t//if allAdds && !opts.IgnoreDup {\n\t//\treturn qs.copyFrom(tx, in, opts)\n\t//}\n\n\tend := \";\"\n\tif opts.IgnoreDup {\n\t\tend = ` ON CONFLICT ` + onConflict + ` DO NOTHING;`\n\t}\n\n\tvar (\n\t\tinsertQuad *sql.Stmt\n\t\terr        error\n\t)\n\tfor _, d := range quads {\n\t\tdirs := make([]interface{}, 0, len(quad.Directions))\n\t\tfor _, h := range d.Quad.Dirs() {\n\t\t\tdirs = append(dirs, csql.NodeHash{ValueHash: h}.SQLValue())\n\t\t}\n\t\tif !d.Del {\n\t\t\tif insertQuad == nil {\n\t\t\t\tinsertQuad, err = tx.Prepare(`INSERT INTO quads(subject_hash, predicate_hash, object_hash, label_hash, ts) VALUES ($1, $2, $3, $4, now())` + end)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\t\t}\n\t\t\t_, err := insertQuad.Exec(dirs...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tif _, ok := err.(*graph.DeltaError); !ok {\n\t\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected quad delete\")\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "graph/sql/postgres/postgres_test.go",
    "content": "//go:build docker\n// +build docker\n\npackage postgres\n\nimport (\n\t\"testing\"\n\n\t\"github.com/lib/pq\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/sql/sqltest\"\n\t\"github.com/cayleygraph/cayley/internal/dock\"\n)\n\nfunc makePostgres(t testing.TB) (string, graph.Options) {\n\tvar conf dock.Config\n\n\tconf.Image = \"postgres:16\"\n\tconf.OpenStdin = true\n\tconf.Tty = true\n\tconf.Env = []string{`POSTGRES_PASSWORD=postgres`}\n\n\taddr := dock.RunAndWait(t, conf, \"5432\", func(addr string) bool {\n\t\tconn, err := pq.Open(`postgres://postgres:postgres@` + addr + `/postgres?sslmode=disable`)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tconn.Close()\n\t\treturn true\n\t})\n\taddr = `postgres://postgres:postgres@` + addr + `/postgres?sslmode=disable`\n\treturn addr, nil\n}\n\nvar conf = &sqltest.Config{\n\tTimeRound: true,\n\tTimeInMcs: true,\n}\n\nfunc TestPostgres(t *testing.T) {\n\tsqltest.TestAll(t, Type, makePostgres, conf)\n}\n\nfunc BenchmarkPostgres(t *testing.B) {\n\tsqltest.BenchmarkAll(t, Type, makePostgres, conf)\n}\n"
  },
  {
    "path": "graph/sql/quadstore.go",
    "content": "package sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/pquads\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/internal/lru\"\n)\n\nfunc registerQuadStore(name, typ string) {\n\tgraph.RegisterQuadStore(name, graph.QuadStoreRegistration{\n\t\tNewFunc: func(addr string, options graph.Options) (graph.QuadStore, error) {\n\t\t\treturn New(typ, addr, options)\n\t\t},\n\t\tUpgradeFunc: nil,\n\t\tInitFunc: func(addr string, options graph.Options) error {\n\t\t\treturn Init(typ, addr, options)\n\t\t},\n\t\tIsPersistent: true,\n\t})\n}\n\nvar _ Value = StringVal(\"\")\n\ntype StringVal string\n\nfunc (v StringVal) SQLValue() interface{} {\n\treturn escapeNullByte(string(v))\n}\n\ntype IntVal int64\n\nfunc (v IntVal) SQLValue() interface{} {\n\treturn int64(v)\n}\n\ntype FloatVal float64\n\nfunc (v FloatVal) SQLValue() interface{} {\n\treturn float64(v)\n}\n\ntype BoolVal bool\n\nfunc (v BoolVal) SQLValue() interface{} {\n\treturn bool(v)\n}\n\ntype TimeVal time.Time\n\nfunc (v TimeVal) SQLValue() interface{} {\n\treturn time.Time(v)\n}\n\ntype NodeHash struct {\n\trefs.ValueHash\n}\n\nfunc (h NodeHash) SQLValue() interface{} {\n\tif !h.Valid() {\n\t\treturn nil\n\t}\n\treturn []byte(h.ValueHash[:])\n}\nfunc (h *NodeHash) Scan(src interface{}) error {\n\tif src == nil {\n\t\t*h = NodeHash{}\n\t\treturn nil\n\t}\n\tb, ok := src.([]byte)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot scan %T to NodeHash\", src)\n\t}\n\tif len(b) == 0 {\n\t\t*h = NodeHash{}\n\t\treturn nil\n\t} else if len(b) != quad.HashSize {\n\t\treturn fmt.Errorf(\"unexpected hash length: %d\", len(b))\n\t}\n\tcopy(h.ValueHash[:], b)\n\treturn nil\n}\n\nfunc HashOf(s quad.Value) NodeHash {\n\treturn NodeHash{refs.HashOf(s)}\n}\n\ntype QuadHashes struct {\n\trefs.QuadHash\n}\n\ntype QuadStore struct {\n\tdb      *sql.DB\n\topt     *Optimizer\n\tflavor  Registration\n\tids     *lru.Cache\n\tsizes   *lru.Cache\n\tnoSizes bool\n\n\tmu    sync.RWMutex\n\tnodes int64\n\tquads int64\n}\n\nfunc connect(addr string, flavor string, opts graph.Options) (*sql.DB, error) {\n\tmaxOpenConnections, err := opts.IntKey(\"maxopenconnections\", -1)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not retrieve maxopenconnections from options: %v\", err)\n\t}\n\n\tmaxIdleConnections, err := opts.IntKey(\"maxidleconnections\", -1)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not retrieve maxIdleConnections from options: %v\", err)\n\t}\n\n\tconnMaxLifetime, err := opts.StringKey(\"connmaxlifetime\", \"\")\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not retrieve connmaxlifetime from options: %v\", err)\n\t}\n\n\tvar connDuration time.Duration\n\tif connMaxLifetime != \"\" {\n\t\tconnDuration, err = time.ParseDuration(connMaxLifetime)\n\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"couldn't parse connmaxlifetime string: %v\", err)\n\t\t}\n\t}\n\n\t// TODO(barakmich): Parse options for more friendly addr\n\tconn, err := sql.Open(flavor, addr)\n\tif err != nil {\n\t\tclog.Errorf(\"Couldn't open database at %s: %#v\", addr, err)\n\t\treturn nil, err\n\t}\n\n\t// \"Open may just validate its arguments without creating a connection to the database.\"\n\t// \"To verify that the data source name is valid, call Ping.\"\n\t// Source: http://golang.org/pkg/database/sql/#Open\n\tif err := conn.Ping(); err != nil {\n\t\tclog.Errorf(\"Couldn't open database at %s: %#v\", addr, err)\n\t\treturn nil, err\n\t}\n\n\tif maxOpenConnections != -1 {\n\t\tconn.SetMaxOpenConns(maxOpenConnections)\n\t}\n\tif maxIdleConnections != -1 {\n\t\tconn.SetMaxIdleConns(maxIdleConnections)\n\t}\n\tif connDuration != 0 {\n\t\tconn.SetConnMaxLifetime(connDuration)\n\t}\n\n\treturn conn, nil\n}\n\nvar nodesColumns = []string{\n\t\"hash\",\n\t\"value\",\n\t\"value_string\",\n\t\"datatype\",\n\t\"language\",\n\t\"iri\",\n\t\"bnode\",\n\t\"value_int\",\n\t\"value_bool\",\n\t\"value_float\",\n\t\"value_time\",\n}\n\nvar nodeInsertColumns = [][]string{\n\t{\"value\"},\n\t{\"value_string\", \"iri\"},\n\t{\"value_string\", \"bnode\"},\n\t{\"value_string\"},\n\t{\"value_string\", \"datatype\"},\n\t{\"value_string\", \"language\"},\n\t{\"value_int\"},\n\t{\"value_bool\"},\n\t{\"value_float\"},\n\t{\"value_time\"},\n}\n\nfunc Init(typ string, addr string, options graph.Options) error {\n\tfl, ok := types[typ]\n\tif !ok {\n\t\treturn fmt.Errorf(\"unsupported sql database: %q\", typ)\n\t}\n\tconn, err := connect(addr, fl.Driver, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Close()\n\n\tnodesSQL := fl.nodesTable()\n\tquadsSQL := fl.quadsTable()\n\tindexes := fl.quadIndexes(options)\n\n\tif fl.NoSchemaChangesInTx {\n\t\t_, err = conn.Exec(nodesSQL)\n\t\tif err != nil {\n\t\t\terr = fl.Error(err)\n\t\t\tclog.Errorf(\"Cannot create nodes table: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\t_, err = conn.Exec(quadsSQL)\n\t\tif err != nil {\n\t\t\terr = fl.Error(err)\n\t\t\tclog.Errorf(\"Cannot create quad table: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tfor _, index := range indexes {\n\t\t\tif _, err = conn.Exec(index); err != nil {\n\t\t\t\tclog.Errorf(\"Cannot create index: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else {\n\t\ttx, err := conn.Begin()\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"Couldn't begin creation transaction: %s\", err)\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = tx.Exec(nodesSQL)\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t\terr = fl.Error(err)\n\t\t\tclog.Errorf(\"Cannot create nodes table: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\t_, err = tx.Exec(quadsSQL)\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t\terr = fl.Error(err)\n\t\t\tclog.Errorf(\"Cannot create quad table: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tfor _, index := range indexes {\n\t\t\tif _, err = tx.Exec(index); err != nil {\n\t\t\t\tclog.Errorf(\"Cannot create index: %v\", err)\n\t\t\t\ttx.Rollback()\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ttx.Commit()\n\t}\n\treturn nil\n}\n\nfunc New(typ string, addr string, options graph.Options) (graph.QuadStore, error) {\n\tfl, ok := types[typ]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unsupported sql database: %q\", typ)\n\t}\n\tconn, err := connect(addr, fl.Driver, options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tqs := &QuadStore{\n\t\tdb:      conn,\n\t\topt:     NewOptimizer(),\n\t\tflavor:  fl,\n\t\tquads:   -1,\n\t\tnodes:   -1,\n\t\tsizes:   lru.New(1024),\n\t\tids:     lru.New(1024),\n\t\tnoSizes: true, // Skip size checking by default.\n\t}\n\tqs.opt.SetRegexpOp(qs.flavor.RegexpOp)\n\tif qs.flavor.NoOffsetWithoutLimit {\n\t\tqs.opt.NoOffsetWithoutLimit()\n\t}\n\n\tif local, err := options.BoolKey(\"local_optimize\", false); err != nil {\n\t\treturn nil, err\n\t} else if ok && local {\n\t\tqs.noSizes = false\n\t}\n\treturn qs, nil\n}\n\nfunc escapeNullByte(s string) string {\n\treturn strings.Replace(s, \"\\u0000\", `\\x00`, -1)\n}\nfunc unescapeNullByte(s string) string {\n\treturn strings.Replace(s, `\\x00`, \"\\u0000\", -1)\n}\n\ntype ValueType int\n\nfunc (t ValueType) Columns() []string {\n\treturn nodeInsertColumns[t]\n}\n\nfunc NodeValues(h NodeHash, v quad.Value) (ValueType, []interface{}, error) {\n\tvar (\n\t\tnodeKey ValueType\n\t\tvalues  = []interface{}{h.SQLValue(), nil, nil}[:1]\n\t)\n\tswitch v := v.(type) {\n\tcase quad.IRI:\n\t\tnodeKey = 1\n\t\tvalues = append(values, string(v), true)\n\tcase quad.BNode:\n\t\tnodeKey = 2\n\t\tvalues = append(values, string(v), true)\n\tcase quad.String:\n\t\tnodeKey = 3\n\t\tvalues = append(values, escapeNullByte(string(v)))\n\tcase quad.TypedString:\n\t\tnodeKey = 4\n\t\tvalues = append(values, escapeNullByte(string(v.Value)), string(v.Type))\n\tcase quad.LangString:\n\t\tnodeKey = 5\n\t\tvalues = append(values, escapeNullByte(string(v.Value)), v.Lang)\n\tcase quad.Int:\n\t\tnodeKey = 6\n\t\tvalues = append(values, int64(v))\n\tcase quad.Bool:\n\t\tnodeKey = 7\n\t\tvalues = append(values, bool(v))\n\tcase quad.Float:\n\t\tnodeKey = 8\n\t\tvalues = append(values, float64(v))\n\tcase quad.Time:\n\t\tnodeKey = 9\n\t\tvalues = append(values, time.Time(v))\n\tdefault:\n\t\tnodeKey = 0\n\t\tp, err := pquads.MarshalValue(v)\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"couldn't marshal value: %v\", err)\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tvalues = append(values, p)\n\t}\n\treturn nodeKey, values, nil\n}\n\nfunc (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {\n\treturn &quadWriter{qs: qs}, nil\n}\n\ntype quadWriter struct {\n\tqs     *QuadStore\n\tdeltas []graph.Delta\n}\n\nfunc (w *quadWriter) WriteQuad(q quad.Quad) error {\n\t_, err := w.WriteQuads([]quad.Quad{q})\n\treturn err\n}\n\nfunc (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {\n\t// TODO(dennwc): write an optimized implementation\n\tw.deltas = w.deltas[:0]\n\tif cap(w.deltas) < len(buf) {\n\t\tw.deltas = make([]graph.Delta, 0, len(buf))\n\t}\n\tfor _, q := range buf {\n\t\tw.deltas = append(w.deltas, graph.Delta{\n\t\t\tQuad: q, Action: graph.Add,\n\t\t})\n\t}\n\terr := w.qs.ApplyDeltas(w.deltas, graph.IgnoreOpts{\n\t\tIgnoreDup: true,\n\t})\n\tw.deltas = w.deltas[:0]\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\tw.deltas = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) ApplyDeltas(in []graph.Delta, opts graph.IgnoreOpts) error {\n\t// first calculate values ref deltas\n\tdeltas := graphlog.SplitDeltas(in)\n\n\ttx, err := qs.db.Begin()\n\tif err != nil {\n\t\tclog.Errorf(\"couldn't begin write transaction: %v\", err)\n\t\treturn err\n\t}\n\n\tretry := qs.flavor.TxRetry\n\tif retry == nil {\n\t\tretry = func(tx *sql.Tx, stmts func() error) error {\n\t\t\treturn stmts()\n\t\t}\n\t}\n\tp := make([]string, 4)\n\tfor i := range p {\n\t\tp[i] = qs.flavor.Placeholder(i + 1)\n\t}\n\n\terr = retry(tx, func() error {\n\t\terr = qs.flavor.RunTx(tx, deltas.IncNode, deltas.QuadAdd, opts)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// quad delete is also generic, execute here\n\t\tvar (\n\t\t\tdeleteQuad   *sql.Stmt\n\t\t\tdeleteTriple *sql.Stmt\n\t\t)\n\t\tfixNodes := make(map[refs.ValueHash]int)\n\t\tfor _, d := range deltas.QuadDel {\n\t\t\tdirs := make([]interface{}, 0, len(quad.Directions))\n\t\t\tfor _, h := range d.Quad.Dirs() {\n\t\t\t\tdirs = append(dirs, NodeHash{h}.SQLValue())\n\t\t\t}\n\t\t\tif deleteQuad == nil {\n\t\t\t\tdeleteQuad, err = tx.Prepare(`DELETE FROM quads WHERE subject_hash=` + p[0] + ` and predicate_hash=` + p[1] + ` and object_hash=` + p[2] + ` and label_hash=` + p[3] + `;`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdeleteTriple, err = tx.Prepare(`DELETE FROM quads WHERE subject_hash=` + p[0] + ` and predicate_hash=` + p[1] + ` and object_hash=` + p[2] + ` and label_hash is null;`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tstmt := deleteQuad\n\t\t\tif i := len(dirs) - 1; dirs[i] == nil {\n\t\t\t\tstmt = deleteTriple\n\t\t\t\tdirs = dirs[:i]\n\t\t\t}\n\t\t\tresult, err := stmt.Exec(dirs...)\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't exec DELETE statement: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\taffected, err := result.RowsAffected()\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't get DELETE RowsAffected: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif affected != 1 {\n\t\t\t\tif !opts.IgnoreMissing {\n\t\t\t\t\t// TODO: reference to delta\n\t\t\t\t\treturn &graph.DeltaError{Err: graph.ErrQuadNotExist}\n\t\t\t\t}\n\t\t\t\t// revert counters for all directions of this quad\n\t\t\t\tfor _, dir := range quad.Directions {\n\t\t\t\t\tif h := d.Quad.Get(dir); h.Valid() {\n\t\t\t\t\t\tfixNodes[h]++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(deltas.DecNode) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\t// node update SQL is generic enough to run it here\n\t\tupdateNode, err := tx.Prepare(`UPDATE nodes SET refs = refs + ` + p[0] + ` WHERE hash = ` + p[1] + `;`)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, n := range deltas.DecNode {\n\t\t\tn.RefInc += fixNodes[n.Hash]\n\t\t\tif n.RefInc == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err := updateNode.Exec(n.RefInc, NodeHash{n.Hash}.SQLValue())\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't exec UPDATE statement: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t// and remove unused nodes at last\n\t\t_, err = tx.Exec(`DELETE FROM nodes WHERE refs <= 0;`)\n\t\tif err != nil {\n\t\t\tclog.Errorf(\"couldn't exec DELETE nodes statement: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\ttx.Rollback()\n\t\treturn err\n\t}\n\n\tqs.mu.Lock()\n\t// TODO(barakmich): Sync size with writes.\n\tqs.quads = -1\n\tqs.nodes = -1\n\tqs.mu.Unlock()\n\treturn tx.Commit()\n}\n\nfunc (qs *QuadStore) Quad(val graph.Ref) (quad.Quad, error) {\n\th := val.(QuadHashes)\n\tvar q quad.Quad\n\tvar err error\n\tq.Subject, err = qs.NameOf(h.Get(quad.Subject))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Predicate, err = qs.NameOf(h.Get(quad.Predicate))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Object, err = qs.NameOf(h.Get(quad.Object))\n\tif err != nil {\n\t\treturn q, err\n\t}\n\tq.Label, err = qs.NameOf(h.Get(quad.Label))\n\treturn q, err\n}\n\nfunc (qs *QuadStore) QuadIterator(d quad.Direction, val graph.Ref) iterator.Shape {\n\tv, ok := val.(Value)\n\tif !ok {\n\t\treturn iterator.NewNull()\n\t}\n\tsel := AllQuads(\"\")\n\tsel.WhereEq(\"\", dirField(d), v)\n\treturn qs.newIterator(sel)\n}\n\nfunc (qs *QuadStore) querySize(ctx context.Context, sel Select) (refs.Size, error) {\n\tsel.Fields = []Field{\n\t\t{Name: \"COUNT(*)\", Raw: true}, // TODO: proper support for expressions\n\t}\n\tvar sz int64\n\terr := qs.QueryRow(ctx, sel).Scan(&sz)\n\tif err != nil {\n\t\treturn refs.Size{}, err\n\t}\n\treturn refs.Size{\n\t\tValue: sz,\n\t\tExact: true,\n\t}, nil\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {\n\tv, ok := val.(Value)\n\tif !ok {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t}\n\tsel := AllQuads(\"\")\n\tsel.WhereEq(\"\", dirField(d), v)\n\treturn qs.querySize(ctx, sel)\n}\n\nfunc (qs *QuadStore) NodesAllIterator() iterator.Shape {\n\treturn qs.newIterator(AllNodes())\n}\n\nfunc (qs *QuadStore) QuadsAllIterator() iterator.Shape {\n\treturn qs.newIterator(AllQuads(\"\"))\n}\n\nfunc (qs *QuadStore) ValueOf(s quad.Value) (graph.Ref, error) {\n\treturn NodeHash(HashOf(s)), nil\n}\n\n// NullTime represents a time.Time that may be null. NullTime implements the\n// sql.Scanner interface so it can be used as a scan destination, similar to\n// sql.NullString.\ntype NullTime struct {\n\tTime  time.Time\n\tValid bool // Valid is true if Time is not NULL\n}\n\n// Scan implements the Scanner interface.\nfunc (nt *NullTime) Scan(value interface{}) error {\n\tif value == nil {\n\t\tnt.Time, nt.Valid = time.Time{}, false\n\t\treturn nil\n\t}\n\tswitch value := value.(type) {\n\tcase time.Time:\n\t\tnt.Time, nt.Valid = value, true\n\tcase []byte:\n\t\tt, err := time.Parse(\"2006-01-02 15:04:05.999999\", string(value))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnt.Time, nt.Valid = t, true\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported time format: %T: %v\", value, value)\n\t}\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (nt NullTime) Value() (driver.Value, error) {\n\tif !nt.Valid {\n\t\treturn nil, nil\n\t}\n\treturn nt.Time, nil\n}\n\nfunc (qs *QuadStore) NameOf(v graph.Ref) (quad.Value, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t} else if v, ok := v.(refs.PreFetchedValue); ok {\n\t\treturn v.NameOf(), nil\n\t}\n\tvar hash NodeHash\n\tswitch h := v.(type) {\n\tcase refs.PreFetchedValue:\n\t\treturn h.NameOf(), nil\n\tcase NodeHash:\n\t\thash = h\n\tcase refs.ValueHash:\n\t\thash = NodeHash{h}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unexpected token: %T\", v)\n\t}\n\tif !hash.Valid() {\n\t\treturn nil, nil\n\t}\n\tif val, ok := qs.ids.Get(hash.String()); ok {\n\t\treturn val.(quad.Value), nil\n\t}\n\tquery := `SELECT\n\t\tvalue,\n\t\tvalue_string,\n\t\tdatatype,\n\t\tlanguage,\n\t\tiri,\n\t\tbnode,\n\t\tvalue_int,\n\t\tvalue_bool,\n\t\tvalue_float,\n\t\tvalue_time\n\tFROM nodes WHERE hash = ` + qs.flavor.Placeholder(1) + ` LIMIT 1;`\n\tc := qs.db.QueryRow(query, hash.SQLValue())\n\tvar (\n\t\tdata        []byte\n\t\tstr         sql.NullString\n\t\ttyp         sql.NullString\n\t\tlang        sql.NullString\n\t\tiri         sql.NullBool\n\t\tbnode       sql.NullBool\n\t\tvint        sql.NullInt64\n\t\tvbool       sql.NullBool\n\t\tvfloat      sql.NullFloat64\n\t\tvtimeStd    sql.NullTime\n\t\tvtimeCustom NullTime\n\t)\n\tvar (\n\t\tvtimeScan  any\n\t\tvtimeTime  *time.Time\n\t\tvtimeValid *bool\n\t)\n\tif qs.flavor.CustomNullTime {\n\t\tvtimeScan = &vtimeCustom\n\t\tvtimeTime = &vtimeCustom.Time\n\t\tvtimeValid = &vtimeCustom.Valid\n\t} else {\n\t\tvtimeScan = &vtimeStd\n\t\tvtimeTime = &vtimeStd.Time\n\t\tvtimeValid = &vtimeStd.Valid\n\t}\n\tif err := c.Scan(\n\t\t&data,\n\t\t&str,\n\t\t&typ,\n\t\t&lang,\n\t\t&iri,\n\t\t&bnode,\n\t\t&vint,\n\t\t&vbool,\n\t\t&vfloat,\n\t\tvtimeScan,\n\t); err != nil {\n\t\tif err != sql.ErrNoRows {\n\t\t\treturn nil, fmt.Errorf(\"error executing value lookup: %w\", err)\n\t\t}\n\t}\n\tvar val quad.Value\n\tif str.Valid {\n\t\tif iri.Bool {\n\t\t\tval = quad.IRI(str.String)\n\t\t} else if bnode.Bool {\n\t\t\tval = quad.BNode(str.String)\n\t\t} else if lang.Valid {\n\t\t\tval = quad.LangString{\n\t\t\t\tValue: quad.String(unescapeNullByte(str.String)),\n\t\t\t\tLang:  lang.String,\n\t\t\t}\n\t\t} else if typ.Valid {\n\t\t\tval = quad.TypedString{\n\t\t\t\tValue: quad.String(unescapeNullByte(str.String)),\n\t\t\t\tType:  quad.IRI(typ.String),\n\t\t\t}\n\t\t} else {\n\t\t\tval = quad.String(unescapeNullByte(str.String))\n\t\t}\n\t} else if vint.Valid {\n\t\tval = quad.Int(vint.Int64)\n\t} else if vbool.Valid {\n\t\tval = quad.Bool(vbool.Bool)\n\t} else if vfloat.Valid {\n\t\tval = quad.Float(vfloat.Float64)\n\t} else if *vtimeValid {\n\t\tval = quad.Time(vtimeTime.UTC())\n\t} else {\n\t\tqv, err := pquads.UnmarshalValue(data)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unmarshal value: %w\", err)\n\t\t}\n\t\tval = qv\n\t}\n\tif val != nil {\n\t\tqs.ids.Put(hash.String(), val)\n\t}\n\treturn val, nil\n}\n\nfunc (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\tst := graph.Stats{\n\t\tNodes: refs.Size{Exact: true},\n\t\tQuads: refs.Size{Exact: true},\n\t}\n\tqs.mu.RLock()\n\tst.Quads.Value = qs.quads\n\tst.Nodes.Value = qs.nodes\n\tqs.mu.RUnlock()\n\tif st.Quads.Value >= 0 {\n\t\treturn st, nil\n\t}\n\tquery := func(table string) string {\n\t\treturn \"SELECT COUNT(*) FROM \" + table + \";\"\n\t}\n\tif !exact && qs.flavor.Estimated != nil {\n\t\tquery = qs.flavor.Estimated\n\t\tst.Quads.Exact = false\n\t\tst.Nodes.Exact = false\n\t}\n\terr := qs.db.QueryRow(query(\"quads\")).Scan(&st.Quads.Value)\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\terr = qs.db.QueryRow(query(\"nodes\")).Scan(&st.Nodes.Value)\n\tif err != nil {\n\t\treturn graph.Stats{}, err\n\t}\n\tif st.Quads.Exact {\n\t\tqs.mu.Lock()\n\t\tqs.quads = st.Quads.Value\n\t\tqs.nodes = st.Nodes.Value\n\t\tqs.mu.Unlock()\n\t}\n\treturn st, nil\n}\n\nfunc (qs *QuadStore) Close() error {\n\treturn qs.db.Close()\n}\n\nfunc (qs *QuadStore) QuadDirection(in graph.Ref, d quad.Direction) (graph.Ref, error) {\n\treturn NodeHash{in.(QuadHashes).Get(d)}, nil\n}\n\nfunc (qs *QuadStore) sizeForIterator(dir quad.Direction, hash NodeHash) int64 {\n\tvar err error\n\tif qs.noSizes {\n\t\tst, _ := qs.Stats(context.TODO(), false)\n\t\tif dir == quad.Predicate {\n\t\t\treturn (st.Quads.Value / 100) + 1\n\t\t}\n\t\treturn (st.Quads.Value / 1000) + 1\n\t}\n\tif val, ok := qs.sizes.Get(hash.String() + string(dir.Prefix())); ok {\n\t\treturn val.(int64)\n\t}\n\tvar size int64\n\tif clog.V(4) {\n\t\tclog.Infof(\"sql: getting size for select %s, %v\", dir.String(), hash)\n\t}\n\terr = qs.db.QueryRow(\n\t\tfmt.Sprintf(\"SELECT count(*) FROM quads WHERE %s_hash = \"+qs.flavor.Placeholder(1)+\";\", dir.String()), hash.SQLValue()).Scan(&size)\n\tif err != nil {\n\t\tclog.Errorf(\"Error getting size from SQL database: %v\", err)\n\t\treturn 0\n\t}\n\tqs.sizes.Put(hash.String()+string(dir.Prefix()), size)\n\treturn size\n}\n"
  },
  {
    "path": "graph/sql/shape.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar DefaultDialect = QueryDialect{\n\tFieldQuote: func(s string) string {\n\t\treturn strconv.Quote(s)\n\t},\n\tPlaceholder: func(_ int) string {\n\t\treturn \"?\"\n\t},\n}\n\ntype QueryDialect struct {\n\tRegexpOp    CmpOp\n\tFieldQuote  func(string) string\n\tPlaceholder func(int) string\n}\n\nfunc NewBuilder(d QueryDialect) *Builder {\n\treturn &Builder{d: d}\n}\n\ntype Builder struct {\n\td  QueryDialect\n\tpi int\n}\n\nfunc needQuotes(s string) bool {\n\tfor i, r := range s {\n\t\tif (r < 'a' || r > 'z') && r != '_' && (i == 0 || r < '0' || r > '9') {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\nfunc (b *Builder) EscapeField(s string) string {\n\tif !needQuotes(s) {\n\t\treturn s\n\t}\n\treturn b.d.FieldQuote(s)\n}\nfunc (b *Builder) Placeholder() string {\n\tb.pi++\n\treturn b.d.Placeholder(b.pi)\n}\n\nconst (\n\ttagPref = \"__\"\n\ttagNode = tagPref + \"node\"\n)\n\nfunc dirField(d quad.Direction) string {\n\treturn d.String() + \"_hash\"\n}\n\nfunc dirTag(d quad.Direction) string {\n\treturn tagPref + d.String()\n}\n\ntype Value interface {\n\tSQLValue() interface{}\n}\n\ntype Shape interface {\n\tSQL(b *Builder) string\n\tArgs() []Value\n\tColumns() []string\n}\n\nfunc AllNodes() Select {\n\treturn Nodes(nil, nil)\n}\n\nfunc Nodes(where []Where, params []Value) Select {\n\treturn Select{\n\t\tFields: []Field{\n\t\t\t{Name: \"hash\", Alias: tagNode},\n\t\t},\n\t\tFrom: []Source{\n\t\t\tTable{Name: \"nodes\"},\n\t\t},\n\t\tWhere:  where,\n\t\tParams: params,\n\t}\n}\n\nfunc AllQuads(alias string) Select {\n\tsel := Select{\n\t\tFrom: []Source{\n\t\t\tTable{Name: \"quads\", Alias: alias},\n\t\t},\n\t}\n\tfor _, d := range quad.Directions {\n\t\tsel.Fields = append(sel.Fields, Field{\n\t\t\tTable: alias,\n\t\t\tName:  dirField(d),\n\t\t\tAlias: dirTag(d),\n\t\t})\n\t}\n\treturn sel\n}\n\ntype FieldName struct {\n\tName  string\n\tTable string\n}\n\nfunc (FieldName) isExpr() {}\nfunc (f FieldName) SQL(b *Builder) string {\n\tname := b.EscapeField(f.Name)\n\tif f.Table != \"\" {\n\t\tname = f.Table + \".\" + name\n\t}\n\treturn name\n}\n\ntype Field struct {\n\tName  string\n\tRaw   bool // do not quote Name\n\tAlias string\n\tTable string\n}\n\nfunc (f Field) SQL(b *Builder) string {\n\tname := f.Name\n\tif !f.Raw {\n\t\tname = b.EscapeField(name)\n\t}\n\tif f.Table != \"\" {\n\t\tname = f.Table + \".\" + name\n\t}\n\tif f.Alias == \"\" {\n\t\treturn name\n\t}\n\treturn name + \" AS \" + b.EscapeField(f.Alias)\n}\nfunc (f Field) NameOrAlias() string {\n\tif f.Alias != \"\" {\n\t\treturn f.Alias\n\t}\n\treturn f.Name\n}\n\ntype Source interface {\n\tSQL(b *Builder) string\n\tArgs() []Value\n\tisSource()\n}\n\ntype Table struct {\n\tName  string\n\tAlias string\n}\n\nfunc (Table) isSource() {}\n\ntype Subquery struct {\n\tQuery Select\n\tAlias string\n}\n\nfunc (Subquery) isSource() {}\nfunc (s Subquery) SQL(b *Builder) string {\n\tq := \"(\" + s.Query.SQL(b) + \")\"\n\tif s.Alias != \"\" {\n\t\tq += \" AS \" + b.EscapeField(s.Alias)\n\t}\n\treturn q\n}\nfunc (s Subquery) Args() []Value {\n\treturn s.Query.Args()\n}\n\nfunc (f Table) SQL(b *Builder) string {\n\tif f.Alias == \"\" {\n\t\treturn f.Name\n\t}\n\treturn f.Name + \" AS \" + b.EscapeField(f.Alias)\n}\n\nfunc (f Table) Args() []Value {\n\treturn nil\n}\nfunc (f Table) NameSQL() string {\n\tif f.Alias != \"\" {\n\t\treturn f.Alias\n\t}\n\treturn f.Name\n}\n\ntype CmpOp string\n\nconst (\n\tOpEqual  = CmpOp(\"=\")\n\tOpGT     = CmpOp(\">\")\n\tOpGTE    = CmpOp(\">=\")\n\tOpLT     = CmpOp(\"<\")\n\tOpLTE    = CmpOp(\"<=\")\n\tOpIsNull = CmpOp(\"IS NULL\")\n\tOpIsTrue = CmpOp(\"IS true\")\n)\n\ntype Expr interface {\n\tisExpr()\n\tSQL(b *Builder) string\n}\n\ntype Placeholder struct{}\n\nfunc (Placeholder) isExpr() {}\n\nfunc (Placeholder) SQL(b *Builder) string {\n\treturn b.Placeholder()\n}\n\ntype Where struct {\n\tField string\n\tTable string\n\tOp    CmpOp\n\tValue Expr\n}\n\nfunc (w Where) SQL(b *Builder) string {\n\tname := w.Field\n\tif w.Table != \"\" {\n\t\tname = w.Table + \".\" + b.EscapeField(name)\n\t}\n\tparts := []string{name, string(w.Op)}\n\tif w.Value != nil {\n\t\tparts = append(parts, w.Value.SQL(b))\n\t}\n\treturn strings.Join(parts, \" \")\n}\n\nvar _ Shape = Select{}\n\n// Select is a simplified representation of SQL SELECT query.\ntype Select struct {\n\tFields []Field\n\tFrom   []Source\n\tWhere  []Where\n\tParams []Value\n\tLimit  int64\n\tOffset int64\n\n\t// TODO(dennwc): this field in unexported because we don't want it to a be a part of the API\n\t//               however, it's necessary to make NodesFrom optimizations to work with SQL\n\tnextPath bool\n}\n\nfunc (s Select) Clone() Select {\n\ts.Fields = append([]Field{}, s.Fields...)\n\ts.From = append([]Source{}, s.From...)\n\ts.Where = append([]Where{}, s.Where...)\n\ts.Params = append([]Value{}, s.Params...)\n\treturn s\n}\n\nfunc (s Select) isAll() bool {\n\treturn len(s.From) == 1 && len(s.Where) == 0 && len(s.Params) == 0 && !s.onlyAsSubquery()\n}\n\n// onlyAsSubquery indicates that query cannot be merged into existing SELECT because of some specific properties of query.\n// An example of such properties might be LIMIT, DISTINCT, etc.\nfunc (s Select) onlyAsSubquery() bool {\n\treturn s.Limit > 0 || s.Offset > 0\n}\n\nfunc (s Select) Columns() []string {\n\tnames := make([]string, 0, len(s.Fields))\n\tfor _, f := range s.Fields {\n\t\tname := f.Alias\n\t\tif name == \"\" {\n\t\t\tname = f.Name\n\t\t}\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\nfunc (s Select) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tsq, ok := qs.(*QuadStore)\n\tif !ok {\n\t\treturn iterator.NewError(fmt.Errorf(\"not a SQL quadstore: %T\", qs))\n\t}\n\treturn sq.newIterator(s)\n}\n\nfunc (s Select) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\t// TODO: call optimize on sub-tables? but what if it decides to de-optimize our SQL shape?\n\treturn s, false\n}\n\nfunc (s *Select) AppendParam(o Value) Expr {\n\ts.Params = append(s.Params, o)\n\treturn Placeholder{}\n}\n\nfunc (s *Select) WhereEq(tbl, field string, v Value) {\n\ts.Where = append(s.Where, Where{\n\t\tTable: tbl,\n\t\tField: field,\n\t\tOp:    OpEqual,\n\t\tValue: s.AppendParam(v),\n\t})\n}\n\nfunc (s Select) SQL(b *Builder) string {\n\tvar parts []string\n\n\tvar fields []string\n\tfor _, f := range s.Fields {\n\t\tfields = append(fields, f.SQL(b))\n\t}\n\tparts = append(parts, \"SELECT \"+strings.Join(fields, \", \"))\n\n\tvar tables []string\n\tfor _, t := range s.From {\n\t\ttables = append(tables, t.SQL(b))\n\t}\n\tparts = append(parts, \"FROM \"+strings.Join(tables, \", \"))\n\n\tif len(s.Where) != 0 {\n\t\tvar wheres []string\n\t\tfor _, w := range s.Where {\n\t\t\twheres = append(wheres, w.SQL(b))\n\t\t}\n\t\tparts = append(parts, \"WHERE \"+strings.Join(wheres, \" AND \"))\n\t}\n\tif s.Limit > 0 {\n\t\tparts = append(parts, \"LIMIT \"+strconv.FormatInt(s.Limit, 10))\n\t}\n\tif s.Offset > 0 {\n\t\tparts = append(parts, \"OFFSET \"+strconv.FormatInt(s.Offset, 10))\n\t}\n\tsep := \" \"\n\tif len(fields) > 1 {\n\t\tsep = \"\\n\\t\"\n\t}\n\treturn strings.Join(parts, sep)\n}\nfunc (s Select) Args() []Value {\n\tvar args []Value\n\t// first add args for FROM subqueries\n\tfor _, q := range s.From {\n\t\targs = append(args, q.Args()...)\n\t}\n\t// and add params for WHERE\n\targs = append(args, s.Params...)\n\treturn args\n}\n"
  },
  {
    "path": "graph/sql/shape_test.go",
    "content": "package sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype stringVal string\n\nfunc (s stringVal) Key() interface{} {\n\treturn string(s)\n}\n\nfunc (s stringVal) SQLValue() interface{} {\n\treturn string(s)\n}\n\nfunc sVal(s string) stringVal {\n\treturn stringVal(s)\n}\n\nfunc sVals(arr ...string) []Value {\n\tout := make([]Value, 0, len(arr))\n\tfor _, s := range arr {\n\t\tout = append(out, sVal(s))\n\t}\n\treturn out\n}\n\nvar shapeCases = []struct {\n\tskip bool\n\tname string\n\ts    shape.Shape\n\tqu   string\n\targs []Value\n}{\n\t{\n\t\tname: \"all nodes\",\n\t\ts:    shape.AllNodes{},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes`,\n\t},\n\t{\n\t\tname: \"lookup iri\",\n\t\ts:    shape.Lookup{quad.IRI(\"a\")},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes WHERE hash = $1`,\n\t\targs: []Value{HashOf(quad.IRI(\"a\"))},\n\t},\n\t{\n\t\tname: \"gt iri\",\n\t\ts: shape.Filter{\n\t\t\tFrom: shape.AllNodes{},\n\t\t\tFilters: []shape.ValueFilter{\n\t\t\t\tshape.Comparison{Op: iterator.CompareGT, Val: quad.IRI(\"a\")},\n\t\t\t},\n\t\t},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes WHERE value_string > $1 AND iri IS true`,\n\t\targs: []Value{StringVal(\"a\")},\n\t},\n\t{\n\t\tname: \"gt string\",\n\t\ts: shape.Filter{\n\t\t\tFrom: shape.AllNodes{},\n\t\t\tFilters: []shape.ValueFilter{\n\t\t\t\tshape.Comparison{Op: iterator.CompareGT, Val: quad.String(\"a\")},\n\t\t\t},\n\t\t},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes WHERE value_string > $1 AND iri IS NULL AND bnode IS NULL AND datatype IS NULL AND language IS NULL`,\n\t\targs: []Value{StringVal(\"a\")},\n\t},\n\t{\n\t\tname: \"gt typed string\",\n\t\ts: shape.Filter{\n\t\t\tFrom: shape.AllNodes{},\n\t\t\tFilters: []shape.ValueFilter{\n\t\t\t\tshape.Comparison{Op: iterator.CompareGT, Val: quad.TypedString{Value: \"a\", Type: \"A\"}},\n\t\t\t},\n\t\t},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes WHERE value_string > $1 AND datatype = $2`,\n\t\targs: []Value{StringVal(\"a\"), StringVal(\"A\")},\n\t},\n\t{\n\t\tname: \"lookup int\",\n\t\ts: shape.Filter{\n\t\t\tFrom: shape.AllNodes{},\n\t\t\tFilters: []shape.ValueFilter{\n\t\t\t\tshape.Comparison{Op: iterator.CompareGT, Val: quad.Int(42)},\n\t\t\t},\n\t\t},\n\t\tqu:   `SELECT hash AS ` + tagNode + ` FROM nodes WHERE value_int > $1`,\n\t\targs: []Value{IntVal(42)},\n\t},\n\t{\n\t\tname: \"all quads\",\n\t\ts:    shape.Quads{},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label\n\tFROM quads AS t_1`,\n\t},\n\t{\n\t\tname: \"limit quads and skip first\",\n\t\ts:    shape.Page{From: shape.Quads{}, Limit: 100, Skip: 1},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label\n\tFROM quads AS t_1\n\tLIMIT 100\n\tOFFSET 1`,\n\t},\n\t{\n\t\tname: \"quads with subject and predicate\",\n\t\ts: shape.Quads{\n\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t{Dir: quad.Predicate, Values: shape.Fixed{sVal(\"p\")}},\n\t\t},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label\n\tFROM quads AS t_1\n\tWHERE t_1.subject_hash = $1 AND t_1.predicate_hash = $2`,\n\t\targs: sVals(\"s\", \"p\"),\n\t},\n\t{\n\t\tname: \"quad actions\",\n\t\ts: shape.QuadsAction{\n\t\t\tResult: quad.Subject,\n\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\tquad.Object: {\"o1\", \"o2\"},\n\t\t\t\tquad.Label:  {\"l 1\"},\n\t\t\t},\n\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT subject_hash AS ` + tagNode + `, object_hash AS o1, object_hash AS o2, label_hash AS \"l 1\"\n\tFROM quads\n\tWHERE predicate_hash = $1`,\n\t\targs: sVals(\"p\"),\n\t},\n\t{\n\t\tname: \"quad actions and save\",\n\t\ts: shape.Save{\n\t\t\tTags: []string{\"sub\"},\n\t\t\tFrom: shape.QuadsAction{\n\t\t\t\tResult: quad.Subject,\n\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\tquad.Object: {\"o1\", \"o2\"},\n\t\t\t\t\tquad.Label:  {\"l 1\"},\n\t\t\t\t},\n\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT subject_hash AS sub, subject_hash AS ` + tagNode + `, object_hash AS o1, object_hash AS o2, label_hash AS \"l 1\"\n\tFROM quads\n\tWHERE predicate_hash = $1`,\n\t\targs: sVals(\"p\"),\n\t},\n\t{\n\t\tname: \"quads with subquery\",\n\t\ts: shape.Quads{\n\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t{\n\t\t\t\tDir: quad.Predicate,\n\t\t\t\tValues: shape.QuadsAction{\n\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label\n\tFROM quads AS t_1, (SELECT subject_hash AS ` + tagNode + ` FROM quads WHERE predicate_hash = $1) AS t_2\n\tWHERE t_1.subject_hash = $2 AND t_1.predicate_hash = t_2.` + tagNode,\n\t\targs: sVals(\"p\", \"s\"),\n\t},\n\t{\n\t\tname: \"quads with subquery (inner tags)\",\n\t\ts: shape.Quads{\n\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t{\n\t\t\t\tDir: quad.Predicate,\n\t\t\t\tValues: shape.Save{\n\t\t\t\t\tTags: []string{\"pred\"},\n\t\t\t\t\tFrom: shape.QuadsAction{\n\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\t\t\tquad.Object: {\"ob\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label, t_2.subject_hash AS pred, t_2.object_hash AS ob\n\tFROM quads AS t_1, quads AS t_2\n\tWHERE t_1.subject_hash = $1 AND t_2.predicate_hash = $2 AND t_1.predicate_hash = t_2.subject_hash`,\n\t\targs: sVals(\"s\", \"p\"),\n\t},\n\t{\n\t\tname: \"quads with subquery (limit)\",\n\t\ts: shape.Quads{\n\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t{\n\t\t\t\tDir: quad.Predicate,\n\t\t\t\tValues: shape.Page{\n\t\t\t\t\tLimit: 10,\n\t\t\t\t\tFrom: shape.QuadsAction{\n\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT t_1.subject_hash AS __subject, t_1.predicate_hash AS __predicate, t_1.object_hash AS __object, t_1.label_hash AS __label\n\tFROM quads AS t_1, (SELECT subject_hash AS ` + tagNode + ` FROM quads WHERE predicate_hash = $1 LIMIT 10) AS t_2\n\tWHERE t_1.subject_hash = $2 AND t_1.predicate_hash = t_2.` + tagNode,\n\t\targs: sVals(\"p\", \"s\"),\n\t},\n\t{\n\t\tskip: true, // TODO\n\t\tname: \"quads with subquery (inner tags + limit)\",\n\t\ts: shape.Quads{\n\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t{\n\t\t\t\tDir: quad.Predicate,\n\t\t\t\tValues: shape.Save{\n\t\t\t\t\tTags: []string{\"pred\"},\n\t\t\t\t\tFrom: shape.Page{\n\t\t\t\t\t\tLimit: 10,\n\t\t\t\t\t\tFrom: shape.QuadsAction{\n\t\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\t\t\t\tquad.Object: {\"ob\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu:   ``,\n\t\targs: []Value{},\n\t},\n\t{\n\t\tname: \"nodes from quads\",\n\t\ts: shape.NodesFrom{\n\t\t\tDir: quad.Object,\n\t\t\tQuads: shape.Quads{\n\t\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t\t{\n\t\t\t\t\tDir: quad.Predicate,\n\t\t\t\t\tValues: shape.QuadsAction{\n\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\t\t\tquad.Object: {\"ob\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\tquad.Predicate: sVal(\"p\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT t_1.object_hash AS ` + tagNode + `, t_2.object_hash AS ob\n\tFROM quads AS t_1, quads AS t_2\n\tWHERE t_1.subject_hash = $1 AND t_2.predicate_hash = $2 AND t_1.predicate_hash = t_2.subject_hash`,\n\t\targs: sVals(\"s\", \"p\"),\n\t},\n\t{\n\t\tname: \"intersect selects\",\n\t\ts: shape.Intersect{\n\t\t\tshape.Save{\n\t\t\t\tTags: []string{\"sub\"},\n\t\t\t\tFrom: shape.QuadsAction{\n\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\t\tquad.Object: {\"o1\"},\n\t\t\t\t\t\tquad.Label:  {\"l 1\"},\n\t\t\t\t\t},\n\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\tquad.Predicate: sVal(\"p1\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tshape.NodesFrom{\n\t\t\t\tDir: quad.Object,\n\t\t\t\tQuads: shape.Quads{\n\t\t\t\t\t{Dir: quad.Subject, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t\t\t{\n\t\t\t\t\t\tDir: quad.Predicate,\n\t\t\t\t\t\tValues: shape.QuadsAction{\n\t\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\t\tSave: map[quad.Direction][]string{\n\t\t\t\t\t\t\t\tquad.Object: {\"ob\"},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\t\tquad.Predicate: sVal(\"p2\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu: `SELECT t_3.subject_hash AS sub, t_3.subject_hash AS __node, t_3.object_hash AS o1, t_3.label_hash AS \"l 1\", t_2.object_hash AS ob\n\tFROM quads AS t_3, quads AS t_1, quads AS t_2\n\tWHERE t_3.predicate_hash = $1 AND t_1.subject_hash = $2 AND t_2.predicate_hash = $3 AND t_1.predicate_hash = t_2.subject_hash AND t_3.subject_hash = t_1.object_hash`,\n\t\targs: sVals(\"p1\", \"s\", \"p2\"),\n\t},\n\t{\n\t\tname: \"deep shape\",\n\t\ts: shape.NodesFrom{\n\t\t\tDir: quad.Object,\n\t\t\tQuads: shape.Quads{\n\t\t\t\tshape.QuadFilter{Dir: quad.Predicate, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t\tshape.QuadFilter{\n\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\tValues: shape.NodesFrom{\n\t\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\t\tQuads: shape.Quads{\n\t\t\t\t\t\t\tshape.QuadFilter{Dir: quad.Predicate, Values: shape.Fixed{sVal(\"s\")}},\n\t\t\t\t\t\t\tshape.QuadFilter{\n\t\t\t\t\t\t\t\tDir: quad.Object,\n\t\t\t\t\t\t\t\tValues: shape.NodesFrom{\n\t\t\t\t\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\t\t\t\t\tQuads: shape.Quads{\n\t\t\t\t\t\t\t\t\t\tshape.QuadFilter{Dir: quad.Predicate, Values: shape.Fixed{sVal(\"a\")}},\n\t\t\t\t\t\t\t\t\t\tshape.QuadFilter{\n\t\t\t\t\t\t\t\t\t\t\tDir: quad.Object,\n\t\t\t\t\t\t\t\t\t\t\tValues: shape.QuadsAction{\n\t\t\t\t\t\t\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\t\t\t\t\t\t\tFilter: map[quad.Direction]graph.Ref{\n\t\t\t\t\t\t\t\t\t\t\t\t\tquad.Predicate: sVal(\"n\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\tquad.Object:    sVal(\"k\"),\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tqu:   `SELECT t_5.object_hash AS __node FROM quads AS t_5, (SELECT t_3.subject_hash AS __node FROM quads AS t_3, (SELECT t_1.subject_hash AS __node FROM quads AS t_1, (SELECT subject_hash AS __node FROM quads WHERE predicate_hash = $1 AND object_hash = $2) AS t_2 WHERE t_1.predicate_hash = $3 AND t_1.object_hash = t_2.__node) AS t_4 WHERE t_3.predicate_hash = $4 AND t_3.object_hash = t_4.__node) AS t_6 WHERE t_5.predicate_hash = $5 AND t_5.subject_hash = t_6.__node`,\n\t\targs: sVals(\"n\", \"k\", \"a\", \"s\", \"s\"),\n\t},\n}\n\nfunc TestSQLShapes(t *testing.T) {\n\tdialect := DefaultDialect\n\tdialect.Placeholder = func(i int) string {\n\t\treturn fmt.Sprintf(\"$%d\", i)\n\t}\n\tfor _, c := range shapeCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\topt := NewOptimizer()\n\t\t\ts, ok := c.s.Optimize(context.TODO(), opt)\n\t\t\tif c.skip {\n\t\t\t\tt.Skipf(\"%#v\", s)\n\t\t\t}\n\t\t\trequire.True(t, ok, \"%#v\", s)\n\t\t\tsq, ok := s.(Shape)\n\t\t\trequire.True(t, ok, \"%#v\", s)\n\t\t\tb := NewBuilder(dialect)\n\t\t\trequire.Equal(t, c.qu, sq.SQL(b), \"%#v\", sq)\n\t\t\trequire.Equal(t, c.args, sq.Args())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "graph/sql/sqlite/sqlite.go",
    "content": "//go:build cgo\n\npackage sqlite\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/quad\"\n\tsqlite3 \"github.com/mattn/go-sqlite3\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\tgraphlog \"github.com/cayleygraph/cayley/graph/log\"\n\tcsql \"github.com/cayleygraph/cayley/graph/sql\"\n)\n\nconst Type = \"sqlite\"\n\nvar QueryDialect = csql.QueryDialect{\n\tRegexpOp: \"REGEXP\",\n\tFieldQuote: func(name string) string {\n\t\treturn \"`\" + name + \"`\"\n\t},\n\tPlaceholder: func(n int) string { return \"?\" },\n}\n\nfunc init() {\n\tregex := func(re, s string) (bool, error) {\n\t\treturn regexp.MatchString(re, s)\n\t}\n\tsql.Register(\"sqlite3-regexp\",\n\t\t&sqlite3.SQLiteDriver{\n\t\t\tConnectHook: func(conn *sqlite3.SQLiteConn) error {\n\t\t\t\treturn conn.RegisterFunc(\"regexp\", regex, true)\n\t\t\t},\n\t\t})\n\tcsql.Register(Type, csql.Registration{\n\t\tDriver:               \"sqlite3-regexp\",\n\t\tHashType:             fmt.Sprintf(`BINARY(%d)`, quad.HashSize),\n\t\tBytesType:            `BLOB`,\n\t\tHorizonType:          `INTEGER`,\n\t\tTimeType:             `DATETIME`,\n\t\tQueryDialect:         QueryDialect,\n\t\tNoOffsetWithoutLimit: true,\n\t\tNoForeignKeys:        true,\n\t\tError: func(err error) error {\n\t\t\treturn err\n\t\t},\n\t\tEstimated: nil,\n\t\tRunTx:     runTxSqlite,\n\t})\n}\n\nfunc runTxSqlite(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error {\n\t// update node ref counts and insert nodes\n\tvar (\n\t\t// prepared statements for each value type\n\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\tupdateValue *sql.Stmt\n\t)\n\tfor _, n := range nodes {\n\t\tif n.RefInc >= 0 {\n\t\t\tnodeKey, values, err := csql.NodeValues(csql.NodeHash{ValueHash: n.Hash}, n.Val)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvalues = append([]interface{}{n.RefInc}, values...)\n\t\t\tvalues = append(values, n.RefInc) // one more time for UPDATE\n\t\t\tstmt, ok := insertValue[nodeKey]\n\t\t\tif !ok {\n\t\t\t\tvar ph = make([]string, len(values)-1) // excluding last increment\n\t\t\t\tfor i := range ph {\n\t\t\t\t\tph[i] = \"?\"\n\t\t\t\t}\n\t\t\t\tstmt, err = tx.Prepare(`INSERT INTO nodes(refs, hash, ` +\n\t\t\t\t\tstrings.Join(nodeKey.Columns(), \", \") +\n\t\t\t\t\t`) VALUES (` + strings.Join(ph, \", \") +\n\t\t\t\t\t`) ON CONFLICT(hash) DO UPDATE SET refs = refs + ?;`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue[nodeKey] = stmt\n\t\t\t}\n\t\t\t_, err = stmt.Exec(values...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected node update\")\n\t\t}\n\t}\n\tfor _, s := range insertValue {\n\t\ts.Close()\n\t}\n\tif s := updateValue; s != nil {\n\t\ts.Close()\n\t}\n\tinsertValue = nil\n\tupdateValue = nil\n\n\t// now we can deal with quads\n\tignore := \"\"\n\tif opts.IgnoreDup {\n\t\tignore = \" OR IGNORE\"\n\t}\n\n\tvar (\n\t\tinsertQuad *sql.Stmt\n\t\terr        error\n\t)\n\tfor _, d := range quads {\n\t\tdirs := make([]interface{}, 0, len(quad.Directions))\n\t\tfor _, h := range d.Quad.Dirs() {\n\t\t\tdirs = append(dirs, csql.NodeHash{ValueHash: h}.SQLValue())\n\t\t}\n\t\tif !d.Del {\n\t\t\tif insertQuad == nil {\n\t\t\t\tinsertQuad, err = tx.Prepare(`INSERT` + ignore + ` INTO quads(subject_hash, predicate_hash, object_hash, label_hash, ts) VALUES (?, ?, ?, ?, datetime());`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinsertValue = make(map[csql.ValueType]*sql.Stmt)\n\t\t\t}\n\t\t\t_, err := insertQuad.Exec(dirs...)\n\t\t\terr = convInsertError(err)\n\t\t\tif err != nil {\n\t\t\t\tif _, ok := err.(*graph.DeltaError); !ok {\n\t\t\t\t\tclog.Errorf(\"couldn't exec INSERT statement: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected quad delete\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc convInsertError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif e, ok := err.(sqlite3.Error); ok {\n\t\tif e.Code == sqlite3.ErrConstraint {\n\t\t\treturn &graph.DeltaError{Err: graph.ErrQuadExists}\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "graph/sql/sqlite/sqlite_test.go",
    "content": "//go:build cgo\n\npackage sqlite\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/sql/sqltest\"\n)\n\nfunc makeSqlite(t testing.TB) (string, graph.Options) {\n\ttmpFile, err := os.CreateTemp(\"\", fmt.Sprintf(\"cayley_test_%s*\", Type))\n\tif err != nil {\n\t\tt.Fatalf(\"Could not create working directory: %v\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tos.RemoveAll(tmpFile.Name())\n\t})\n\treturn fmt.Sprintf(\"file:%s?_loc=UTC\", tmpFile.Name()), nil\n}\n\nvar conf = &sqltest.Config{\n\tTimeRound: true,\n\tTimeInMcs: false,\n}\n\nfunc TestSqlite(t *testing.T) {\n\tsqltest.TestAll(t, Type, makeSqlite, conf)\n}\n\nfunc BenchmarkSqlite(t *testing.B) {\n\tsqltest.BenchmarkAll(t, Type, makeSqlite, conf)\n}\n"
  },
  {
    "path": "graph/sql/sqltest/sqltest.go",
    "content": "package sqltest\n\nimport (\n\t\"testing\"\n\t\"unicode/utf8\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/graph/sql\"\n)\n\ntype Config struct {\n\tTimeRound bool\n\tTimeInMcs bool\n}\n\nfunc (c Config) quadStore() *graphtest.Config {\n\treturn &graphtest.Config{\n\t\tNoPrimitives:        true,\n\t\tTimeInMcs:           c.TimeInMcs,\n\t\tTimeRound:           c.TimeRound,\n\t\tOptimizesComparison: true,\n\t}\n}\n\nfunc TestAll(t *testing.T, typ string, fnc DatabaseFunc, c *Config) {\n\tif c == nil {\n\t\tc = &Config{TimeInMcs: true}\n\t}\n\tcreate := makeDatabaseFunc(typ, fnc)\n\tt.Run(\"qs\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tgraphtest.TestAll(t, create, c.quadStore())\n\t})\n\tt.Run(\"zero rune\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttestZeroRune(t, create)\n\t})\n}\n\nfunc BenchmarkAll(t *testing.B, typ string, fnc DatabaseFunc, c *Config) {\n\tif c == nil {\n\t\tc = &Config{}\n\t}\n\tcreate := makeDatabaseFunc(typ, fnc)\n\tt.Run(\"qs\", func(t *testing.B) {\n\t\tgraphtest.BenchmarkAll(t, create, c.quadStore())\n\t})\n}\n\ntype DatabaseFunc func(t testing.TB) (string, graph.Options)\n\nfunc makeDatabaseFunc(typ string, create DatabaseFunc) testutil.DatabaseFunc {\n\treturn func(t testing.TB) (graph.QuadStore, graph.Options) {\n\t\taddr, opts := create(t)\n\t\tif err := sql.Init(typ, addr, opts); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tqs, err := sql.New(typ, addr, opts)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tt.Cleanup(func() {\n\t\t\tqs.Close()\n\t\t})\n\t\treturn qs, nil\n\t}\n}\n\nfunc testZeroRune(t testing.TB, create testutil.DatabaseFunc) {\n\tqs, opts := create(t)\n\n\tw := testutil.MakeWriter(t, qs, opts)\n\n\tobj := quad.String(\"AB\\u0000CD\")\n\tif !utf8.ValidString(string(obj)) {\n\t\tt.Fatal(\"invalid utf8\")\n\t}\n\n\terr := w.AddQuad(quad.Quad{\n\t\tSubject:   quad.IRI(\"bob\"),\n\t\tPredicate: quad.IRI(\"pred\"),\n\t\tObject:    obj,\n\t})\n\trequire.NoError(t, err)\n\tqsv, err := qs.ValueOf(quad.Raw(obj.String()))\n\trequire.NoError(t, err)\n\tqsn, err := qs.NameOf(qsv)\n\trequire.NoError(t, err)\n\trequire.Equal(t, obj, qsn)\n}\n"
  },
  {
    "path": "graph/transaction.go",
    "content": "// Copyright 2015 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage graph\n\nimport \"github.com/cayleygraph/quad\"\n\n// Transaction stores a bunch of Deltas to apply together in an atomic step on the database.\ntype Transaction struct {\n\t// Deltas stores the deltas in the right order\n\tDeltas []Delta\n\t// deltas stores the deltas in a map to avoid duplications\n\tdeltas map[Delta]struct{}\n}\n\n// NewTransaction initialize a new transaction.\nfunc NewTransaction() *Transaction {\n\treturn NewTransactionN(10)\n}\n\n// NewTransactionN initialize a new transaction with a predefined capacity.\nfunc NewTransactionN(n int) *Transaction {\n\treturn &Transaction{Deltas: make([]Delta, 0, n), deltas: make(map[Delta]struct{}, n)}\n}\n\n// AddQuad adds a new quad to the transaction if it is not already present in it.\n// If there is a 'remove' delta for that quad, it will remove that delta from\n// the transaction instead of actually adding the quad.\nfunc (t *Transaction) AddQuad(q quad.Quad) {\n\tad, rd := createDeltas(q)\n\n\tif _, adExists := t.deltas[ad]; !adExists {\n\t\tif _, rdExists := t.deltas[rd]; rdExists {\n\t\t\tt.deleteDelta(rd)\n\t\t} else {\n\t\t\tt.addDelta(ad)\n\t\t}\n\t}\n}\n\n// RemoveQuad adds a quad to remove to the transaction.\n// The quad will be removed from the database if it is not present in the\n// transaction, otherwise it simply remove it from the transaction.\nfunc (t *Transaction) RemoveQuad(q quad.Quad) {\n\tad, rd := createDeltas(q)\n\n\tif _, adExists := t.deltas[ad]; adExists {\n\t\tt.deleteDelta(ad)\n\t} else {\n\t\tif _, rdExists := t.deltas[rd]; !rdExists {\n\t\t\tt.addDelta(rd)\n\t\t}\n\t}\n}\n\nfunc createDeltas(q quad.Quad) (ad, rd Delta) {\n\tad = Delta{\n\t\tQuad:   q,\n\t\tAction: Add,\n\t}\n\trd = Delta{\n\t\tQuad:   q,\n\t\tAction: Delete,\n\t}\n\treturn\n}\n\nfunc (t *Transaction) addDelta(d Delta) {\n\tt.Deltas = append(t.Deltas, d)\n\tt.deltas[d] = struct{}{}\n}\n\nfunc (t *Transaction) deleteDelta(d Delta) {\n\tdelete(t.deltas, d)\n\n\tfor i, id := range t.Deltas {\n\t\tif id == d {\n\t\t\tt.Deltas = append(t.Deltas[:i], t.Deltas[i+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "graph/transaction_test.go",
    "content": "package graph\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc TestTransaction(t *testing.T) {\n\tvar tx *Transaction\n\n\t// simples adds / removes\n\ttx = NewTransaction()\n\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"F\", nil))\n\ttx.AddQuad(quad.Make(\"F\", \"follows\", \"G\", nil))\n\ttx.RemoveQuad(quad.Make(\"A\", \"follows\", \"Z\", nil))\n\tif len(tx.Deltas) != 3 {\n\t\tt.Errorf(\"Expected 3 Deltas, have %d delta(s)\", len(tx.Deltas))\n\t}\n\n\t// add, remove -> nothing\n\ttx = NewTransaction()\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\tif len(tx.Deltas) != 0 {\n\t\tt.Errorf(\"Expected [add, remove]->[], have %d Deltas\", len(tx.Deltas))\n\t}\n\n\t// remove, add -> nothing\n\ttx = NewTransaction()\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\tif len(tx.Deltas) != 0 {\n\t\tt.Errorf(\"Expected [add, remove]->[], have %d delta(s)\", len(tx.Deltas))\n\t}\n\n\t// add x2 -> add x1\n\ttx = NewTransaction()\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\tif len(tx.Deltas) != 1 {\n\t\tt.Errorf(\"Expected [add, add]->[add], have %d delta(s)\", len(tx.Deltas))\n\t}\n\n\t// remove x2 -> remove x1\n\ttx = NewTransaction()\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\tif len(tx.Deltas) != 1 {\n\t\tt.Errorf(\"Expected [remove, remove]->[remove], have %d delta(s)\", len(tx.Deltas))\n\t}\n\n\t// add, remove x2 -> remove x1\n\ttx = NewTransaction()\n\ttx.AddQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\ttx.RemoveQuad(quad.Make(\"E\", \"follows\", \"G\", nil))\n\tif len(tx.Deltas) != 1 {\n\t\tt.Errorf(\"Expected [add, remove, remove]->[remove], have %d delta(s)\", len(tx.Deltas))\n\t}\n}\n"
  },
  {
    "path": "imports.go",
    "content": "package cayley\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar (\n\tStartMorphism = path.StartMorphism\n\tStartPath     = path.StartPath\n\n\tNewTransaction = graph.NewTransaction\n)\n\ntype Iterator = iterator.Shape\ntype QuadStore = graph.QuadStore\ntype QuadWriter = graph.QuadWriter\n\ntype Path = path.Path\n\ntype Handle struct {\n\tgraph.QuadStore\n\tgraph.QuadWriter\n}\n\nfunc (h *Handle) Close() error {\n\terr := h.QuadWriter.Close()\n\th.QuadStore.Close()\n\treturn err\n}\n\nfunc Triple(subject, predicate, object interface{}) quad.Quad {\n\treturn Quad(subject, predicate, object, nil)\n}\n\nfunc Quad(subject, predicate, object, label interface{}) quad.Quad {\n\treturn quad.Make(subject, predicate, object, label)\n}\n\nfunc NewGraph(name, dbpath string, opts graph.Options) (*Handle, error) {\n\tqs, err := graph.NewQuadStore(name, dbpath, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tqw, err := graph.NewQuadWriter(\"single\", qs, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Handle{qs, qw}, nil\n}\n\nfunc NewMemoryGraph() (*Handle, error) {\n\treturn NewGraph(\"memstore\", \"\", nil)\n}\n"
  },
  {
    "path": "inference/inference.go",
    "content": "// Package inference implements an in-memory store for inference.\n//\n// RDFS Rules:\n//\n//\t\t1. (x p y) -> (p rdf:type rdf:Property)\n//\t\t2. (p rdfs:domain c), (x p y) -> (x rdf:type c)\n//\t\t3. (p rdfs:range c), (x p y) -> (y rdf:type c)\n//\t\t4a. (x p y) -> (x rdf:type rdfs:Resource)\n//\t\t4b. (x p y) -> (y rdf:type rdfs:Resource)\n//\t\t5. (p rdfs:subPropertyOf q), (q rdfs:subPropertyOf r) -> (p rdfs:subPropertyOf r)\n//\t\t6. (p rdf:type Property) -> (p rdfs:subPropertyOf p)\n//\t\t7. (p rdf:subPropertyOf q), (x p y) -> (x q y)\n//\t\t8. (c rdf:type rdfs:Class) -> (c rdfs:subClassOf rdfs:Resource)\n//\t\t9. (c rdfs:subClassOf d), (x rdf:type c) -> (x rdf:type d)\n//\t\t10. (c rdf:type rdfs:Class) -> (c rdfs:subClassOf c)\n//\t\t11. (c rdfs:subClassOf d), (d rdfs:subClassOf e) -> (c rdfs:subClassOf e)\n//\t\t12. (p rdf:type rdfs:ContainerMembershipProperty) -> (p rdfs:subPropertyOf rdfs:member)\n//\t\t13. (x rdf:type rdfs:Datatype) -> (x rdfs:subClassOf rdfs:Literal)\n//\n// Exported from: https://www.researchgate.net/figure/RDF-RDFS-entailment-rules_tbl1_268419911\n//\n// Implemented here: 1 2 3 5 6 8 10 11\npackage inference\n\nimport (\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n\t\"github.com/cayleygraph/quad/voc/rdfs\"\n)\n\n// classSet is a set of RDF Classes\ntype classSet map[*Class]struct{}\n\n// propertySet is a set of RDF Properties\ntype propertySet map[*Property]struct{}\n\n// Class represents a RDF Class with the links to classes and other properties\ntype Class struct {\n\tstore      *Store\n\tname       quad.Value\n\texplicit   bool\n\treferences int\n\tsuper      classSet\n\tsub        classSet\n\townProp    propertySet\n\tinProp     propertySet\n}\n\nfunc (s *Store) newClass(name quad.Value, explicit bool) *Class {\n\tc := &Class{\n\t\tstore:    s,\n\t\tname:     name,\n\t\texplicit: explicit,\n\t\tsuper:    make(classSet),\n\t\tsub:      make(classSet),\n\t\townProp:  make(propertySet),\n\t\tinProp:   make(propertySet),\n\t}\n\ts.classes[name] = c\n\treturn c\n}\n\n// Name returns the class's name\nfunc (c *Class) Name() quad.Value {\n\treturn c.name\n}\n\n// IsSubClassOf recursively checks whether class is a superClass\nfunc (c *Class) IsSubClassOf(super *Class) bool {\n\tif c == super {\n\t\treturn true\n\t}\n\tif super.name == quad.IRI(rdfs.Resource) {\n\t\treturn true\n\t}\n\tif _, ok := c.super[super]; ok {\n\t\treturn true\n\t}\n\tfor s := range c.super {\n\t\tif s.IsSubClassOf(super) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (c *Class) isReferenced() bool {\n\treturn c.explicit || len(c.super) > 0 ||\n\t\tlen(c.sub) > 0 ||\n\t\tlen(c.ownProp) > 0 ||\n\t\tlen(c.inProp) > 0 ||\n\t\tc.references > 0\n}\n\nfunc (c *Class) deleteIfUnreferenced() {\n\tif c != nil && !c.isReferenced() {\n\t\tc.store.deleteClass(c.name)\n\t}\n}\n\n// Property represents a RDF Property with the links to classes and other properties\ntype Property struct {\n\tname       quad.Value\n\texplicit   bool\n\treferences int\n\tdomain     *Class\n\tprange     *Class\n\tsuper      propertySet\n\tsub        propertySet\n\tstore      *Store\n}\n\nfunc newProperty(name quad.Value, explicit bool, store *Store) *Property {\n\treturn &Property{\n\t\tname:     name,\n\t\texplicit: explicit,\n\t\tsuper:    make(propertySet),\n\t\tsub:      make(propertySet),\n\t\tstore:    store,\n\t}\n}\n\n// Name returns the property's name\nfunc (p *Property) Name() quad.Value {\n\treturn p.name\n}\n\n// Domain returns the domain of the property\nfunc (p *Property) Domain() *Class {\n\treturn p.domain\n}\n\n// Range returns the range of the property\nfunc (p *Property) Range() *Class {\n\treturn p.prange\n}\n\n// IsSubPropertyOf recursively checks whether property is a superProperty\nfunc (p *Property) IsSubPropertyOf(super *Property) bool {\n\tif p == super {\n\t\treturn true\n\t}\n\tif _, ok := p.super[super]; ok {\n\t\treturn true\n\t}\n\tfor s := range p.super {\n\t\tif s.IsSubPropertyOf(super) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *Property) isReferenced() bool {\n\treturn p.explicit || p.references > 0 ||\n\t\tlen(p.super) > 0 ||\n\t\tlen(p.sub) > 0 ||\n\t\tp.domain != nil ||\n\t\tp.prange != nil\n}\n\nfunc (p *Property) deleteIfUnreferenced() {\n\tif p != nil && !p.isReferenced() {\n\t\tp.store.deleteProperty(p.name)\n\t}\n}\n\n// Store is a struct holding the inference data\ntype Store struct {\n\tclasses    map[quad.Value]*Class\n\tproperties map[quad.Value]*Property\n}\n\n// NewStore creates a new Store\nfunc NewStore() Store {\n\ts := Store{\n\t\tclasses:    make(map[quad.Value]*Class),\n\t\tproperties: make(map[quad.Value]*Property),\n\t}\n\ts.ensureClass(quad.IRI(rdfs.Resource))\n\treturn s\n}\n\n// GetClass returns a class struct for class name, if it doesn't exist in the store then it returns nil\nfunc (s *Store) GetClass(name quad.Value) *Class {\n\treturn s.classes[name]\n}\n\n// GetProperty returns a class struct for property name, if it doesn't exist in the store then it returns nil\nfunc (s *Store) GetProperty(name quad.Value) *Property {\n\treturn s.properties[name]\n}\n\nfunc (s *Store) ensureClass(name quad.Value) {\n\tif c, ok := s.classes[name]; ok {\n\t\tc.explicit = true\n\t} else {\n\t\t_ = s.newClass(name, true)\n\t}\n}\n\nfunc (s *Store) getOrCreateImplicitClass(name quad.Value) *Class {\n\tc, ok := s.classes[name]\n\tif !ok {\n\t\tc = s.newClass(name, false)\n\t}\n\treturn c\n}\n\nfunc (s *Store) createProperty(name quad.Value) {\n\tif property, ok := s.properties[name]; ok {\n\t\tproperty.explicit = true\n\t\treturn\n\t}\n\ts.properties[name] = newProperty(name, true, s)\n}\n\nfunc (s *Store) getOrCreateImplicitProperty(name quad.Value) *Property {\n\tif p, ok := s.properties[name]; ok {\n\t\treturn p\n\t}\n\tp := newProperty(name, false, s)\n\ts.properties[name] = p\n\treturn p\n}\n\nfunc (s *Store) addClassRelationship(child quad.Value, parent quad.Value) {\n\tp := s.getOrCreateImplicitClass(parent)\n\tc := s.getOrCreateImplicitClass(child)\n\tif _, ok := p.sub[c]; !ok {\n\t\tp.sub[c] = struct{}{}\n\t\tc.super[p] = struct{}{}\n\t}\n}\n\nfunc (s *Store) addPropertyRelationship(child quad.Value, parent quad.Value) {\n\tp := s.getOrCreateImplicitProperty(parent)\n\tc := s.getOrCreateImplicitProperty(child)\n\tif _, ok := p.sub[c]; !ok {\n\t\tp.sub[c] = struct{}{}\n\t\tc.super[p] = struct{}{}\n\t}\n}\n\nfunc (s *Store) setPropertyDomain(property quad.Value, domain quad.Value) {\n\tp := s.getOrCreateImplicitProperty(property)\n\tc := s.getOrCreateImplicitClass(domain)\n\t// FIXME(iddan): Currently doesn't support multiple domains as they are very rare\n\tp.domain = c\n\tc.ownProp[p] = struct{}{}\n}\n\nfunc (s *Store) setPropertyRange(property quad.Value, prange quad.Value) {\n\tp := s.getOrCreateImplicitProperty(property)\n\tc := s.getOrCreateImplicitClass(prange)\n\tp.prange = c\n\t// FIXME(iddan): Currently doesn't support multiple ranges as they are very rare\n\tc.inProp[p] = struct{}{}\n}\n\nfunc (s *Store) addClassInstance(name quad.Value) {\n\tc := s.GetClass(name)\n\tif c == nil {\n\t\tc = s.getOrCreateImplicitClass(name)\n\t}\n\tc.references++\n}\n\nfunc (s *Store) addPropertyInstance(name quad.Value) *Property {\n\tp := s.GetProperty(name)\n\tif p == nil {\n\t\tp = s.getOrCreateImplicitProperty(name)\n\t}\n\tp.references++\n\treturn p\n}\n\n// ProcessQuads is used to update the store with multiple quads\nfunc (s *Store) ProcessQuads(quads ...quad.Quad) {\n\tfor _, q := range quads {\n\t\ts.processQuad(q)\n\t}\n}\n\n// processQuad is used to update the store with a new quad\nfunc (s *Store) processQuad(q quad.Quad) {\n\tpred, ok := q.Predicate.(quad.IRI)\n\tif !ok {\n\t\treturn\n\t}\n\tsub, obj := q.Subject, q.Object\n\tswitch pred {\n\tcase rdf.Type:\n\t\tswitch obj := obj.(type) {\n\t\tcase quad.BNode:\n\t\t\ts.addClassInstance(obj)\n\t\tcase quad.IRI:\n\t\t\tswitch obj {\n\t\t\tcase rdfs.Class:\n\t\t\t\ts.ensureClass(sub)\n\t\t\tcase rdf.Property:\n\t\t\t\ts.createProperty(sub)\n\t\t\tdefault:\n\t\t\t\ts.addClassInstance(obj)\n\t\t\t}\n\t\t}\n\tcase rdfs.SubPropertyOf:\n\t\ts.addPropertyRelationship(sub, obj)\n\tcase rdfs.SubClassOf:\n\t\ts.addClassRelationship(sub, obj)\n\tcase rdfs.Domain:\n\t\ts.setPropertyDomain(sub, obj)\n\tcase rdfs.Range:\n\t\ts.setPropertyRange(sub, obj)\n\tdefault:\n\t\tp := s.addPropertyInstance(pred)\n\t\tdomain := p.Domain()\n\t\tif domain != nil {\n\t\t\tdomain.references++\n\t\t}\n\t\tprange := p.Range()\n\t\tif prange != nil {\n\t\t\tprange.references++\n\t\t}\n\t}\n}\n\nfunc (s *Store) deleteClass(name quad.Value) {\n\tc, ok := s.classes[name]\n\tif !ok {\n\t\treturn\n\t}\n\tfor sub := range c.sub {\n\t\tdelete(sub.super, c)\n\t}\n\tfor super := range c.super {\n\t\tdelete(super.sub, c)\n\t}\n\tdelete(s.classes, name)\n}\n\nfunc (s *Store) deleteProperty(name quad.Value) {\n\tp, ok := s.properties[name]\n\tif !ok {\n\t\treturn\n\t}\n\tfor super := range p.super {\n\t\tdelete(super.sub, p)\n\t}\n\tfor sub := range p.sub {\n\t\tdelete(sub.super, p)\n\t}\n\tdelete(s.properties, name)\n}\n\nfunc (s *Store) deleteClassRel(child quad.Value, parent quad.Value) {\n\tp := s.GetClass(parent)\n\tc := s.GetClass(child)\n\tif _, ok := p.sub[c]; ok {\n\t\tdelete(p.sub, c)\n\t\tdelete(c.super, p)\n\t\tp.deleteIfUnreferenced()\n\t\tc.deleteIfUnreferenced()\n\t}\n}\n\nfunc (s *Store) deletePropertyRel(child quad.Value, parent quad.Value) {\n\tp := s.GetProperty(parent)\n\tc := s.GetProperty(child)\n\tif _, ok := p.sub[c]; ok {\n\t\tdelete(p.sub, c)\n\t\tdelete(c.super, p)\n\t\tp.deleteIfUnreferenced()\n\t\tc.deleteIfUnreferenced()\n\t}\n}\n\nfunc (s *Store) unsetPropertyDomain(property quad.Value, domain quad.Value) {\n\tp := s.GetProperty(property)\n\tc := s.GetClass(domain)\n\t// FIXME(iddan): Currently doesn't support multiple domains as they are very rare\n\tp.domain = nil\n\tdelete(c.ownProp, p)\n\tp.deleteIfUnreferenced()\n\tc.deleteIfUnreferenced()\n}\n\nfunc (s *Store) unsetPropertyRange(property quad.Value, prange quad.Value) {\n\tp := s.GetProperty(property)\n\tc := s.GetClass(prange)\n\tp.prange = nil\n\t// FIXME(iddan): Currently doesn't support multiple ranges as they are very rare\n\tdelete(c.inProp, p)\n\tp.deleteIfUnreferenced()\n\tc.deleteIfUnreferenced()\n}\n\nfunc (s *Store) deleteClassInstance(name quad.Value) {\n\tc := s.GetClass(name)\n\tif c == nil {\n\t\treturn\n\t}\n\tc.references--\n\tc.deleteIfUnreferenced()\n}\n\nfunc (s *Store) deletePropertyInstance(name quad.Value) *Property {\n\tp := s.GetProperty(name)\n\tif p == nil {\n\t\treturn nil\n\t}\n\tp.references--\n\tp.deleteIfUnreferenced()\n\treturn p\n}\n\n// UnprocessQuads is used to delete multiple quads from the store\nfunc (s *Store) UnprocessQuads(quads ...quad.Quad) {\n\tfor _, q := range quads {\n\t\ts.unprocessQuad(q)\n\t}\n}\n\n// unprocessQuad is used to delete a quad from the store\nfunc (s *Store) unprocessQuad(q quad.Quad) {\n\tpred, ok := q.Predicate.(quad.IRI)\n\tif !ok {\n\t\treturn\n\t}\n\tsub, obj := q.Subject, q.Object\n\tswitch pred {\n\tcase rdf.Type:\n\t\tobj, ok := obj.(quad.IRI)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tswitch obj {\n\t\tcase rdfs.Class:\n\t\t\ts.deleteClass(sub)\n\t\tcase rdf.Property:\n\t\t\ts.deleteProperty(sub)\n\t\tdefault:\n\t\t\ts.deleteClassInstance(obj)\n\t\t}\n\tcase rdfs.SubPropertyOf:\n\t\ts.deletePropertyRel(sub, obj)\n\tcase rdfs.SubClassOf:\n\t\ts.deleteClassRel(sub, obj)\n\tcase rdfs.Domain:\n\t\ts.unsetPropertyDomain(sub, obj)\n\tcase rdfs.Range:\n\t\ts.unsetPropertyRange(sub, obj)\n\tdefault:\n\t\tp := s.deletePropertyInstance(pred)\n\t\tif p != nil {\n\t\t\tif domain := p.Domain(); domain != nil {\n\t\t\t\ts.deleteClassInstance(domain.Name())\n\t\t\t}\n\t\t\tif prange := p.Range(); prange != nil {\n\t\t\t\ts.deleteClassInstance(prange.Name())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "inference/inference_test.go",
    "content": "package inference\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n\t\"github.com/cayleygraph/quad/voc/rdfs\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc triple(subject quad.Value, predicate quad.IRI, object quad.Value) quad.Quad {\n\treturn quad.Quad{Subject: subject, Predicate: predicate, Object: object}\n}\n\nvar (\n\tdomain           = quad.IRI(rdfs.Domain)\n\tprange           = quad.IRI(rdfs.Range)\n\tptype            = quad.IRI(rdf.Type)\n\tclass            = quad.IRI(rdfs.Class)\n\tliteral          = quad.IRI(rdfs.Literal)\n\tproperty         = quad.IRI(rdf.Property)\n\tsubClassOf       = quad.IRI(rdfs.SubClassOf)\n\tsubPropertyOf    = quad.IRI(rdfs.SubPropertyOf)\n\talice            = quad.IRI(\"alice\")\n\taliceName        = quad.String(\"Alice\")\n\tbob              = quad.IRI(\"bob\")\n\tengineer         = quad.IRI(\"Engineer\")\n\tinformation      = quad.IRI(\"information\")\n\tlikes            = quad.IRI(\"likes\")\n\tname             = quad.IRI(\"name\")\n\tperson           = quad.IRI(\"Person\")\n\tpersonal         = quad.IRI(\"personal\")\n\tsoftwareEngineer = quad.IRI(\"SoftwareEngineer\")\n)\nvar (\n\taliceIsPerson                    = triple(alice, ptype, person)\n\taliceLikesBob                    = triple(alice, likes, bob)\n\tlikesRangePerson                 = triple(likes, prange, person)\n\tengineerClass                    = triple(engineer, ptype, class)\n\tengineerSubClass                 = triple(engineer, subClassOf, person)\n\tnameDomainPerson                 = triple(name, domain, person)\n\tnameProperty                     = triple(name, ptype, property)\n\tnameSubPropertyOfPersonal        = triple(name, subPropertyOf, personal)\n\tpersonalProperty                 = triple(personal, ptype, property)\n\tpersonalSubPropertyOfInformation = triple(personal, subPropertyOf, information)\n\tpersonClass                      = triple(person, ptype, class)\n\tsoftwareEngineerClass            = triple(softwareEngineer, ptype, class)\n\taliceNameAlice                   = triple(alice, name, aliceName)\n)\nvar (\n\tengineerAndSoftwareEngineerSubClasses = []quad.Quad{\n\t\tengineerSubClass,\n\t\ttriple(softwareEngineer, subClassOf, engineer),\n\t}\n\tengineerAndPersonClasses = []quad.Quad{\n\t\tengineerClass,\n\t\tpersonClass,\n\t}\n)\n\nfunc TestClassName(t *testing.T) {\n\tiri := alice\n\tc := Class{name: iri}\n\trequire.Equal(t, c.Name(), iri, \"Name was not set correctly for the class\")\n}\n\nfunc TestPropertyName(t *testing.T) {\n\tiri := likes\n\tp := Property{name: iri}\n\trequire.Equal(t, p.Name(), iri, \"Name was not set correctly for the property\")\n}\n\nfunc TestReferencedType(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceIsPerson\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(person)\n\trequire.NotNil(t, createdClass, \"Class was not created\")\n}\n\nfunc TestReferencedBNodeType(t *testing.T) {\n\tstore := NewStore()\n\tname := quad.BNode(\"123\")\n\tq := triple(alice, ptype, name)\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(name)\n\trequire.NotNil(t, createdClass, \"Class was not created\")\n}\n\nfunc TestReferencedProperty(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceLikesBob\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(likes)\n\trequire.NotNil(t, createdProperty, \"Property was not created\")\n}\n\nfunc TestNewClass(t *testing.T) {\n\tstore := NewStore()\n\tq := personClass\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(person)\n\trequire.NotNil(t, createdClass, \"Class was not created\")\n}\n\nfunc TestNewBNodeClass(t *testing.T) {\n\tstore := NewStore()\n\tname := quad.BNode(\"123\")\n\tq := triple(name, ptype, class)\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(name)\n\trequire.NotNil(t, createdClass, \"Class was not created\")\n}\n\nfunc TestInvalidNewClass(t *testing.T) {\n\tstore := NewStore()\n\tname := quad.String(\"Foo\")\n\tq := triple(alice, ptype, name)\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(name)\n\trequire.Nil(t, createdClass, \"Invalid class was created\")\n}\n\nfunc TestNewProperty(t *testing.T) {\n\tstore := NewStore()\n\tq := nameProperty\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\trequire.NotNil(t, createdProperty, \"Property was not created\")\n}\n\nfunc TestInvalidNewProperty(t *testing.T) {\n\tstore := NewStore()\n\tname := quad.String(\"Foo\")\n\tq := quad.Quad{Subject: alice, Predicate: name, Object: bob}\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\trequire.Nil(t, createdProperty, \"Invalid property was created\")\n}\n\nfunc TestSubClass(t *testing.T) {\n\tstore := NewStore()\n\tq := engineerSubClass\n\tstore.ProcessQuads(q)\n\tcreatedClass := store.GetClass(engineer)\n\tcreatedSuperClass := store.GetClass(person)\n\trequire.NotNil(t, createdClass, \"Class was not created\")\n\trequire.NotNil(t, createdSuperClass, \"Super class was not created\")\n\tif _, ok := createdClass.super[createdSuperClass]; !ok {\n\t\tt.Error(\"Super class was not registered for class\")\n\t}\n\tif _, ok := createdSuperClass.sub[createdClass]; !ok {\n\t\tt.Error(\"Class was not registered for super class\")\n\t}\n}\n\nfunc TestSubProperty(t *testing.T) {\n\tstore := NewStore()\n\tq := nameSubPropertyOfPersonal\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\tcreatedSuperProperty := store.GetProperty(personal)\n\trequire.NotNil(t, createdProperty, \"Property was not created\")\n\trequire.NotNil(t, createdSuperProperty, \"Super property was not created\")\n\tif _, ok := createdProperty.super[createdSuperProperty]; !ok {\n\t\tt.Error(\"Super property was not registered for property\")\n\t}\n\tif _, ok := createdSuperProperty.sub[createdProperty]; !ok {\n\t\tt.Error(\"Property was not registered for super property\")\n\t}\n}\n\nfunc TestPropertyDomain(t *testing.T) {\n\tstore := NewStore()\n\tq := nameDomainPerson\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\tcreatedClass := store.GetClass(person)\n\trequire.NotNil(t, createdProperty, \"Property was not created\")\n\trequire.NotNil(t, createdClass, \"Domain class was not created\")\n\tif createdProperty.Domain() != createdClass {\n\t\tt.Error(\"Domain class was not registered for property\")\n\t}\n\tif _, ok := createdClass.ownProp[createdProperty]; !ok {\n\t\tt.Error(\"Property was not registered for class\")\n\t}\n}\n\nfunc TestPropertyRange(t *testing.T) {\n\tstore := NewStore()\n\tq := likesRangePerson\n\tstore.ProcessQuads(q)\n\tcreatedProperty := store.GetProperty(likes)\n\tcreatedClass := store.GetClass(person)\n\trequire.NotNil(t, createdProperty, \"Property was not created\")\n\trequire.NotNil(t, createdClass, \"Range class was not created\")\n\tif createdProperty.Range() != createdClass {\n\t\tt.Error(\"Range class was not registered for property\")\n\t}\n\tif _, ok := createdClass.inProp[createdProperty]; !ok {\n\t\tt.Error(\"Property was not registered for class\")\n\t}\n}\n\nfunc TestIsSubClassOf(t *testing.T) {\n\tstore := NewStore()\n\tq := engineerSubClass\n\tstore.ProcessQuads(q)\n\tif !store.GetClass(engineer).IsSubClassOf(store.GetClass(person)) {\n\t\tt.Error(\"Class was not registered as subclass of super class\")\n\t}\n}\n\nfunc TestIsSubClassOfRecursive(t *testing.T) {\n\tstore := NewStore()\n\tquads := engineerAndSoftwareEngineerSubClasses\n\tstore.ProcessQuads(quads...)\n\tif !store.GetClass(softwareEngineer).IsSubClassOf(store.GetClass(person)) {\n\t\tt.Error(\"Class was not registered as subclass of super class\")\n\t}\n}\n\nfunc TestIsSubClassOfItself(t *testing.T) {\n\tstore := NewStore()\n\tq := personClass\n\tstore.ProcessQuads(q)\n\tif !store.GetClass(person).IsSubClassOf(store.GetClass(person)) {\n\t\tt.Error(\"IsSubClassOf itself doesn't work\")\n\t}\n}\n\nfunc TestIsSubClassOfResource(t *testing.T) {\n\tstore := NewStore()\n\tq := personClass\n\tstore.ProcessQuads(q)\n\tif !store.GetClass(person).IsSubClassOf(store.GetClass(quad.IRI(rdfs.Resource))) {\n\t\tt.Error(\"ItSubClassOf rdfs:Resource doesn't work\")\n\t}\n}\n\nfunc TestIsSubPropertyOf(t *testing.T) {\n\tstore := NewStore()\n\tq := nameSubPropertyOfPersonal\n\tstore.ProcessQuads(q)\n\tif !store.GetProperty(name).IsSubPropertyOf(store.GetProperty(personal)) {\n\t\tt.Error(\"Property was not registered as subproperty of super property\")\n\t}\n}\n\nfunc TestIsSubPropertyOfRecursive(t *testing.T) {\n\tstore := NewStore()\n\tquads := []quad.Quad{\n\t\tnameSubPropertyOfPersonal,\n\t\tpersonalSubPropertyOfInformation,\n\t}\n\tstore.ProcessQuads(quads...)\n\tif !store.GetProperty(name).IsSubPropertyOf(store.GetProperty(information)) {\n\t\tt.Error(\"Property was not registered as subproperty of super property\")\n\t}\n}\n\nfunc TestIsSubPropertyOfItself(t *testing.T) {\n\tstore := NewStore()\n\tq := nameProperty\n\tstore.ProcessQuads(q)\n\tif !store.GetProperty(name).IsSubPropertyOf(store.GetProperty(name)) {\n\t\tt.Error(\"IsSubPropertyOf itself doesn't work\")\n\t}\n}\n\nfunc TestUnprocessInvalidQuad(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(quad.Quad{Subject: alice, Predicate: quad.String(\"Foo\"), Object: person})\n}\n\nfunc TestUnprocessInvalidTypeQuad(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(quad.Quad{Subject: alice, Predicate: ptype, Object: quad.String(\"Foo\")})\n}\n\nfunc TestDeleteReferencedType(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceIsPerson\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedClass := store.GetClass(person)\n\trequire.Nil(t, createdClass, \"Class was not deleted\")\n}\n\nfunc TestDeleteClassWithSubClass(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tengineerClass,\n\t\tengineerSubClass,\n\t)\n\tq := personClass\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tsubClass := store.GetClass(engineer)\n\tif len(subClass.super) != 0 {\n\t\tt.Error(\"Class was not unreferenced\")\n\t}\n}\n\nfunc TestDeleteClassWithSuperClass(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tpersonClass,\n\t\tengineerSubClass,\n\t)\n\tq := engineerClass\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tsuperClass := store.GetClass(person)\n\tif len(superClass.sub) != 0 {\n\t\tt.Error(\"Class was not unreferenced\")\n\t}\n}\n\nfunc TestDeleteNewClass(t *testing.T) {\n\tstore := NewStore()\n\tq := personClass\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedClass := store.GetClass(person)\n\trequire.Nil(t, createdClass, \"Class was not deleted\")\n}\n\nfunc TestDeleteNewProperty(t *testing.T) {\n\tstore := NewStore()\n\tq := nameProperty\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\trequire.Nil(t, createdProperty, \"Property was not deleted\")\n}\n\nfunc TestDeletePropertyWithSubProperty(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tnameSubPropertyOfPersonal,\n\t)\n\tq := personalProperty\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tsubProperty := store.GetProperty(name)\n\tif len(subProperty.super) != 0 {\n\t\tt.Error(\"Property was not unreferenced\")\n\t}\n}\n\nfunc TestDeletePropertyWithSuperProperty(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tpersonalProperty,\n\t\tnameSubPropertyOfPersonal,\n\t)\n\tq := nameProperty\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tsuperProperty := store.GetProperty(personal)\n\tif len(superProperty.sub) != 0 {\n\t\tt.Error(\"Property was not unreferenced\")\n\t}\n}\n\nfunc TestDeleteSubClass(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(engineerAndPersonClasses...)\n\tq := engineerSubClass\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedClass := store.GetClass(engineer)\n\tcreatedSuperClass := store.GetClass(person)\n\t// TODO(iddan): what about garbage collection?\n\tif _, ok := createdClass.super[createdSuperClass]; ok {\n\t\tt.Error(\"Super class was not unregistered for class\")\n\t}\n\tif _, ok := createdSuperClass.sub[createdClass]; ok {\n\t\tt.Error(\"Class was not unregistered for super class\")\n\t}\n}\n\nfunc TestDeleteSubProperty(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tpersonalProperty,\n\t)\n\tq := nameSubPropertyOfPersonal\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\tcreatedSuperProperty := store.GetProperty(personal)\n\t// TODO(iddan): what about garbage collection?\n\tif _, ok := createdProperty.super[createdSuperProperty]; ok {\n\t\tt.Error(\"Super property was not unregistered for property\")\n\t}\n\tif _, ok := createdSuperProperty.sub[createdProperty]; ok {\n\t\tt.Error(\"Property was not unregistered for super property\")\n\t}\n}\n\nfunc TestDeletePropertyDomain(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tpersonClass,\n\t)\n\tq := nameDomainPerson\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\tcreatedClass := store.GetClass(person)\n\t// TODO(iddan): what about garbage collection?\n\tif createdProperty.Domain() == createdClass {\n\t\tt.Error(\"Domain class was not unregistered for property\")\n\t}\n\tif _, ok := createdClass.ownProp[createdProperty]; ok {\n\t\tt.Error(\"Property was not unregistered for class\")\n\t}\n}\n\nfunc TestDeletePropertyRange(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tquad.Quad{Subject: literal, Predicate: ptype, Object: class},\n\t)\n\tq := quad.Quad{Subject: name, Predicate: prange, Object: literal}\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tcreatedProperty := store.GetProperty(name)\n\tcreatedClass := store.GetClass(literal)\n\t// TODO(iddan): what about garbage collection?\n\tif createdProperty.Range() == createdClass {\n\t\tt.Error(\"Range class was not unregistered for property\")\n\t}\n\tif _, ok := createdClass.inProp[createdProperty]; ok {\n\t\tt.Error(\"Property was not unregistered for class\")\n\t}\n}\n\nfunc TestDeleteIsSubClassOf(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(engineerAndPersonClasses...)\n\tq := engineerSubClass\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tif store.GetClass(engineer).IsSubClassOf(store.GetClass(person)) {\n\t\tt.Error(\"Class was not unregistered as subclass of super class\")\n\t}\n}\n\nfunc TestDeleteIsSubClassOfRecursive(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tengineerClass,\n\t\tpersonClass,\n\t\tsoftwareEngineerClass,\n\t)\n\tquads := engineerAndSoftwareEngineerSubClasses\n\tstore.ProcessQuads(quads...)\n\tstore.UnprocessQuads(quads...)\n\tif store.GetClass(softwareEngineer).IsSubClassOf(store.GetClass(person)) {\n\t\tt.Error(\"Class was not unregistered as subclass of super class\")\n\t}\n}\n\nfunc TestDeleteIsSubPropertyOf(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tpersonalProperty,\n\t)\n\tq := nameSubPropertyOfPersonal\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\tif store.GetProperty(name).IsSubPropertyOf(store.GetProperty(personal)) {\n\t\tt.Error(\"Property was not unregistered as subproperty of super property\")\n\t}\n}\n\nfunc TestDeleteIsSubPropertyOfRecursive(t *testing.T) {\n\tstore := NewStore()\n\tstore.ProcessQuads(\n\t\tnameProperty,\n\t\tpersonalProperty,\n\t\tquad.Quad{Subject: information, Predicate: ptype, Object: property},\n\t)\n\tquads := []quad.Quad{\n\t\tnameSubPropertyOfPersonal,\n\t\tpersonalSubPropertyOfInformation,\n\t}\n\tstore.ProcessQuads(quads...)\n\tstore.UnprocessQuads(quads...)\n\tif store.GetProperty(name).IsSubPropertyOf(store.GetProperty(information)) {\n\t\tt.Error(\"Property was not unregistered as subproperty of super property\")\n\t}\n}\n\nfunc TestClassIsReference(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceIsPerson\n\tstore.ProcessQuads(q)\n\tclass := store.GetClass(person)\n\tif !class.isReferenced() {\n\t\tt.Error(\"Class should be referenced\")\n\t}\n}\n\nfunc TestPropertyIsReference(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceLikesBob\n\tstore.ProcessQuads(q)\n\tproperty := store.GetProperty(likes)\n\tif !property.isReferenced() {\n\t\tt.Error(\"Property should be referenced\")\n\t}\n}\n\nfunc TestClassUnreference(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceIsPerson\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\trequire.Nil(t, store.GetClass(person), \"class was not garbage collected\")\n}\n\nfunc TestPropertyUnreference(t *testing.T) {\n\tstore := NewStore()\n\tq := aliceLikesBob\n\tstore.ProcessQuads(q)\n\tstore.UnprocessQuads(q)\n\trequire.Nil(t, store.GetProperty(likes), \"property was not garbage collected\")\n}\n\nfunc TestDomainClassInstance(t *testing.T) {\n\tstore := NewStore()\n\tquads := []quad.Quad{\n\t\tnameDomainPerson,\n\t\taliceNameAlice,\n\t}\n\tstore.ProcessQuads(quads...)\n\tclass := store.GetClass(person)\n\trequire.NotNil(t, class)\n\trequire.True(t, class.isReferenced())\n\trequire.Equal(t, 1, class.references)\n\tstore.UnprocessQuads(aliceNameAlice)\n\trequire.True(t, class.isReferenced())\n\trequire.Equal(t, 0, class.references)\n\tstore.UnprocessQuads(nameDomainPerson)\n\trequire.False(t, class.isReferenced())\n\trequire.Equal(t, 0, class.references)\n}\n\nfunc TestRangeClassInstance(t *testing.T) {\n\tstore := NewStore()\n\tquads := []quad.Quad{\n\t\tlikesRangePerson,\n\t\taliceLikesBob,\n\t}\n\tstore.ProcessQuads(quads...)\n\tclass := store.GetClass(person)\n\trequire.NotNil(t, class)\n\trequire.Equal(t, 1, class.references)\n\trequire.True(t, class.isReferenced())\n\tstore.UnprocessQuads(aliceLikesBob)\n\trequire.Equal(t, 0, class.references)\n\trequire.True(t, class.isReferenced())\n\tstore.UnprocessQuads(likesRangePerson)\n\trequire.Equal(t, 0, class.references)\n\trequire.False(t, class.isReferenced())\n}\n\nfunc TestDeleteNonExistingClass(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(personClass)\n}\n\nfunc TestDeleteNonExistingProperty(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(personalProperty)\n}\n\nfunc TestDeleteNonExistingClassInstance(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(aliceIsPerson)\n}\n\nfunc TestDeleteNonExistingUsedProperty(t *testing.T) {\n\tstore := NewStore()\n\tstore.UnprocessQuads(aliceNameAlice)\n}\n"
  },
  {
    "path": "internal/decompressor/decompressor.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage decompressor\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"compress/bzip2\"\n\t\"compress/gzip\"\n\t\"io\"\n)\n\nconst (\n\tgzipMagic  = \"\\x1f\\x8b\"\n\tb2zipMagic = \"BZh\"\n)\n\n// New detects the file type of an io.Reader between\n// bzip, gzip, or raw quad file.\nfunc New(r io.Reader) (io.Reader, error) {\n\tbr := bufio.NewReader(r)\n\tbuf, err := br.Peek(3)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tswitch {\n\tcase bytes.Equal(buf[:2], []byte(gzipMagic)):\n\t\treturn gzip.NewReader(br)\n\tcase bytes.Equal(buf[:3], []byte(b2zipMagic)):\n\t\treturn bzip2.NewReader(br), nil\n\tdefault:\n\t\treturn br, nil\n\t}\n}\n"
  },
  {
    "path": "internal/decompressor/decompressor_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage decompressor\n\nimport (\n\t\"bytes\"\n\t\"compress/bzip2\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n)\n\nvar testDecompressor = []struct {\n\tmessage string\n\tinput   io.Reader\n\texpect  []byte\n\terr     error\n\treadErr error\n}{\n\t{\n\t\tmessage: \"text input\",\n\t\tinput:   strings.NewReader(\"cayley data\\n\"),\n\t\terr:     nil,\n\t\texpect:  []byte(\"cayley data\\n\"),\n\t\treadErr: nil,\n\t},\n\t{\n\t\tmessage: \"gzip input\",\n\t\tinput: bytes.NewReader([]byte{\n\t\t\t0x1f, 0x8b, 0x08, 0x00, 0x5c, 0xbc, 0xcd, 0x53, 0x00, 0x03, 0x4b, 0x4e, 0xac, 0xcc, 0x49, 0xad,\n\t\t\t0x54, 0x48, 0x49, 0x2c, 0x49, 0xe4, 0x02, 0x00, 0x03, 0xe1, 0xfc, 0xc3, 0x0c, 0x00, 0x00, 0x00,\n\t\t}),\n\t\terr:     nil,\n\t\texpect:  []byte(\"cayley data\\n\"),\n\t\treadErr: nil,\n\t},\n\t{\n\t\tmessage: \"bzip2 input\",\n\t\tinput: bytes.NewReader([]byte{\n\t\t\t0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xb5, 0x4b, 0xe3, 0xc4, 0x00, 0x00,\n\t\t\t0x02, 0xd1, 0x80, 0x00, 0x10, 0x40, 0x00, 0x2e, 0x04, 0x04, 0x20, 0x20, 0x00, 0x31, 0x06, 0x4c,\n\t\t\t0x41, 0x4c, 0x1e, 0xa7, 0xa9, 0x2a, 0x18, 0x26, 0xb1, 0xc2, 0xee, 0x48, 0xa7, 0x0a, 0x12, 0x16,\n\t\t\t0xa9, 0x7c, 0x78, 0x80,\n\t\t}),\n\t\terr:     nil,\n\t\texpect:  []byte(\"cayley data\\n\"),\n\t\treadErr: nil,\n\t},\n\t{\n\t\tmessage: \"bad gzip input\",\n\t\tinput:   strings.NewReader(\"\\x1f\\x8bcayley data\\n\"),\n\t\terr:     gzip.ErrHeader,\n\t\texpect:  nil,\n\t\treadErr: nil,\n\t},\n\t{\n\t\tmessage: \"bad bzip2 input\",\n\t\tinput:   strings.NewReader(\"\\x42\\x5a\\x68cayley data\\n\"),\n\t\terr:     nil,\n\t\texpect:  nil,\n\t\treadErr: bzip2.StructuralError(\"invalid compression level\"),\n\t},\n}\n\nfunc TestDecompressor(t *testing.T) {\n\tfor _, test := range testDecompressor {\n\t\tr, err := New(test.input)\n\t\tif err != test.err {\n\t\t\tt.Fatalf(\"Unexpected error for %s, got:%v expect:%v\", test.message, err, test.err)\n\t\t}\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tp := make([]byte, len(test.expect)*2)\n\t\tn, err := r.Read(p)\n\t\tif err != test.readErr && err != io.EOF {\n\t\t\tt.Fatalf(\"Unexpected error for reading %s, got:%v expect:%v\", test.message, err, test.err)\n\t\t}\n\t\tif !bytes.Equal(p[:n], test.expect) {\n\t\t\tt.Errorf(\"Unexpected read result for %s, got:%q expect:%q\", test.message, p[:n], test.expect)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/dock/dock.go",
    "content": "//go:build docker\n// +build docker\n\npackage dock\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\tdocker \"github.com/fsouza/go-dockerclient\"\n)\n\nvar (\n\tAddress = `unix:///var/run/docker.sock`\n)\n\ntype Config struct {\n\tdocker.Config\n}\n\ntype fullConfig struct {\n\tdocker.Config\n\tdocker.HostConfig\n}\n\nfunc run(t testing.TB, conf fullConfig) (addr string) {\n\tif testing.Short() {\n\t\tt.SkipNow()\n\t}\n\tcli, err := docker.NewClient(Address)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// If there is not relevant image at local, pull image from remote repository.\n\tif err := cli.PullImage(\n\t\tdocker.PullImageOptions{\n\t\t\tRepository: conf.Image,\n\t\t},\n\t\tdocker.AuthConfiguration{},\n\t); err != nil {\n\t\t// If pull image fail, skip the test.\n\t\tt.Skip(err)\n\t}\n\n\tcont, err := cli.CreateContainer(docker.CreateContainerOptions{\n\t\tConfig:     &conf.Config,\n\t\tHostConfig: &conf.HostConfig,\n\t})\n\tif err != nil {\n\t\tt.Skip(err)\n\t}\n\tt.Cleanup(func() {\n\t\tcli.RemoveContainer(docker.RemoveContainerOptions{\n\t\t\tID:    cont.ID,\n\t\t\tForce: true,\n\t\t})\n\t})\n\n\tif err := cli.StartContainer(cont.ID, &conf.HostConfig); err != nil {\n\t\tt.Skip(err)\n\t}\n\n\tinfo, err := cli.InspectContainer(cont.ID)\n\tif err != nil {\n\t\tt.Skip(err)\n\t}\n\taddr = info.NetworkSettings.IPAddress\n\treturn\n}\n\nfunc randPort() int {\n\tconst (\n\t\tmin = 10000\n\t\tmax = 30000\n\t)\n\tfor {\n\t\tport := min + rand.Intn(max-min)\n\t\tc, err := net.DialTimeout(\"tcp\", fmt.Sprintf(\"%s:%d\", localhost, port), time.Second)\n\t\tif c != nil {\n\t\t\tc.Close()\n\t\t}\n\t\tif err != nil {\n\t\t\t// TODO: check for a specific error\n\t\t\treturn port\n\t\t}\n\t}\n}\n\nconst localhost = \"127.0.0.1\"\n\nfunc RunAndWait(t testing.TB, conf Config, port string, check func(string) bool) (addr string) {\n\tfconf := fullConfig{Config: conf.Config}\n\tif runtime.GOOS != \"linux\" {\n\t\tlport := strconv.Itoa(randPort())\n\t\t// nothing except Linux runs Docker natively,\n\t\t// so we randomize the port and expose it on Docker VM\n\t\tfconf.PortBindings = map[docker.Port][]docker.PortBinding{\n\t\t\tdocker.Port(port + \"/tcp\"): {{\n\t\t\t\tHostIP:   localhost,\n\t\t\t\tHostPort: lport,\n\t\t\t}},\n\t\t}\n\t\tport = lport\n\t}\n\taddr = run(t, fconf)\n\tif runtime.GOOS != \"linux\" {\n\t\t// VM ports are automatically exposed on localhost\n\t\taddr = localhost\n\t}\n\taddr += \":\" + port\n\tif check == nil {\n\t\tcheck = waitPort\n\t}\n\tok := false\n\tfor i := 0; i < 10 && !ok; i++ {\n\t\tok = check(addr)\n\t\tif !ok {\n\t\t\ttime.Sleep(time.Second * 2)\n\t\t}\n\t}\n\tif !ok {\n\t\tt.Fatal(\"Container check fails.\")\n\t}\n\treturn addr\n}\n\nconst wait = time.Second * 5\n\nfunc waitPort(addr string) bool {\n\tstart := time.Now()\n\tc, err := net.DialTimeout(\"tcp\", addr, wait)\n\tif err == nil {\n\t\tc.Close()\n\t} else if dt := time.Since(start); dt < wait {\n\t\ttime.Sleep(wait - dt)\n\t}\n\treturn err == nil\n}\n"
  },
  {
    "path": "internal/gephi/stream.go",
    "content": "package gephi\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n\t\"github.com/cayleygraph/quad/voc/rdfs\"\n\t\"github.com/cayleygraph/quad/voc/schema\"\n)\n\nconst (\n\tdefaultLimit = 10000\n\tdefaultSize  = 20\n\tlimitCoord   = 500\n)\n\nconst (\n\tiriInlinePred = quad.IRI(\"gephi:inline\")\n\tiriPosX       = quad.IRI(\"gephi:x\")\n\tiriPosY       = quad.IRI(\"gephi:y\")\n)\n\nvar defaultInline = []quad.IRI{\n\tiriPosX, iriPosY,\n\n\trdf.Type,\n\trdfs.Label,\n\tschema.Name,\n\tschema.UrlProp,\n}\n\ntype GraphStreamHandler struct {\n\tQS graph.QuadStore\n}\n\ntype valHash [quad.HashSize]byte\n\ntype GraphStream struct {\n\tseen map[valHash]int\n\tbuf  *bytes.Buffer\n\tw    io.Writer\n}\n\nfunc printNodeID(id int) string {\n\treturn strconv.FormatInt(int64(id), 16)\n}\n\nfunc NewGraphStream(w io.Writer) *GraphStream {\n\treturn &GraphStream{\n\t\tw:    w,\n\t\tseen: make(map[valHash]int),\n\t\tbuf:  bytes.NewBuffer(nil),\n\t}\n}\nfunc toNodeLabel(v quad.Value) string {\n\tif v == nil {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprint(v.Native())\n}\n\nfunc randCoord() float64 {\n\treturn (rand.Float64() - 0.5) * limitCoord * 2\n}\nfunc randPos() (x float64, y float64) {\n\tx = randCoord()\n\tx2 := x * x\n\tfor y = randCoord(); x2+y*y > limitCoord*limitCoord; y = randCoord() {\n\t}\n\treturn\n}\n\nfunc setStringProp(v *string, props map[quad.Value]quad.Value, name quad.IRI) {\n\tif p, ok := props[name]; ok {\n\t\tif s, ok := p.Native().(string); ok {\n\t\t\t*v = s\n\t\t}\n\t}\n}\n\nfunc (gs *GraphStream) makeOneNode(id string, v quad.Value, props map[quad.Value]quad.Value) map[string]streamNode {\n\tx, y := randPos()\n\tvar xok, yok bool\n\tif p, ok := props[iriPosX]; ok {\n\t\txok = true\n\t\tswitch p := p.(type) {\n\t\tcase quad.Int:\n\t\t\tx = float64(p)\n\t\tcase quad.Float:\n\t\t\tx = float64(p)\n\t\tdefault:\n\t\t\txok = false\n\t\t}\n\t}\n\tif p, ok := props[iriPosY]; ok {\n\t\tyok = true\n\t\tswitch p := p.(type) {\n\t\tcase quad.Int:\n\t\t\ty = float64(p)\n\t\tcase quad.Float:\n\t\t\ty = float64(p)\n\t\tdefault:\n\t\t\tyok = false\n\t\t}\n\t}\n\tvar slabel string\n\tsetStringProp(&slabel, props, rdfs.Label)\n\tsetStringProp(&slabel, props, schema.Name)\n\n\tvar label interface{}\n\tif slabel != \"\" {\n\t\tlabel = slabel\n\t} else {\n\t\tlabel = v.Native()\n\t}\n\n\tnode := streamNode{\n\t\t\"label\": label,\n\t\t\"size\":  defaultSize, \"x\": x, \"y\": y,\n\t}\n\tfor k, v := range props {\n\t\tif k == nil || v == nil ||\n\t\t\t(k == iriPosX && xok) ||\n\t\t\t(k == iriPosY && yok) {\n\t\t\tcontinue\n\t\t}\n\t\tnode[toNodeLabel(k)] = toNodeLabel(v)\n\t}\n\treturn map[string]streamNode{id: node}\n}\nfunc (gs *GraphStream) AddNode(v quad.Value, props map[quad.Value]quad.Value) string {\n\tvar h valHash\n\tquad.HashTo(v, h[:])\n\treturn gs.addNode(v, h, props)\n}\nfunc (gs *GraphStream) encode(o interface{}) {\n\tdata, _ := json.Marshal(o)\n\tgs.buf.Write(data)\n\t// Gephi requires \\r character at the end of each line\n\tgs.buf.WriteString(\"\\r\\n\")\n}\nfunc (gs *GraphStream) addNode(v quad.Value, h valHash, props map[quad.Value]quad.Value) string {\n\tid, ok := gs.seen[h]\n\tif ok {\n\t\treturn printNodeID(id)\n\t} else if v == nil {\n\t\treturn \"\"\n\t}\n\tid = len(gs.seen)\n\tgs.seen[h] = id\n\tsid := printNodeID(id)\n\n\tm := gs.makeOneNode(sid, v, props)\n\tgs.encode(graphStreamEvent{AddNodes: m})\n\treturn sid\n}\nfunc (gs *GraphStream) ChangeNode(v quad.Value, sid string, props map[quad.Value]quad.Value) {\n\tm := gs.makeOneNode(sid, v, props)\n\tgs.encode(graphStreamEvent{ChangeNodes: m})\n}\nfunc (gs *GraphStream) AddEdge(i int, s, o string, p quad.Value) {\n\tid := \"q\" + strconv.FormatInt(int64(i), 16)\n\tps := toNodeLabel(p)\n\tgs.encode(graphStreamEvent{\n\t\tAddEdges: map[string]streamEdge{id: {\n\t\t\tSubject:   s,\n\t\t\tPredicate: ps, Label: ps,\n\t\t\tObject: o,\n\t\t}},\n\t})\n}\nfunc (gs *GraphStream) Flush() error {\n\tif gs.buf.Len() == 0 {\n\t\treturn nil\n\t}\n\t_, err := gs.buf.WriteTo(gs.w)\n\tif err == nil {\n\t\tgs.buf.Reset()\n\t}\n\treturn err\n}\n\ntype streamNode map[string]interface{}\ntype streamEdge struct {\n\tSubject   string `json:\"source\"`\n\tLabel     string `json:\"label\"`\n\tPredicate string `json:\"pred\"`\n\tObject    string `json:\"target\"`\n}\ntype graphStreamEvent struct {\n\tAddNodes    map[string]streamNode `json:\"an,omitempty\"`\n\tChangeNodes map[string]streamNode `json:\"cn,omitempty\"`\n\tDelNodes    map[string]streamNode `json:\"dn,omitempty\"`\n\n\tAddEdges    map[string]streamEdge `json:\"ae,omitempty\"`\n\tChangeEdges map[string]streamEdge `json:\"ce,omitempty\"`\n\tDelEdges    map[string]streamEdge `json:\"de,omitempty\"`\n}\n\nfunc (s *GraphStreamHandler) serveRawQuads(ctx context.Context, gs *GraphStream, quads shape.Shape, limit int) {\n\tit := shape.BuildIterator(ctx, s.QS, quads).Iterate()\n\tdefer it.Close()\n\n\tvar sh, oh valHash\n\tfor i := 0; (limit < 0 || i < limit) && it.Next(ctx); i++ {\n\t\tqv := it.Result()\n\t\tif qv == nil {\n\t\t\tcontinue\n\t\t}\n\t\tq, err := s.QS.Quad(qv)\n\t\tif err != nil {\n\t\t\t// TODO: no error handling\n\t\t\tclog.Warningf(\"error fetching quad value: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tquad.HashTo(q.Subject, sh[:])\n\t\tquad.HashTo(q.Object, oh[:])\n\t\ts, o := gs.addNode(q.Subject, sh, nil), gs.addNode(q.Object, oh, nil)\n\t\tif s == \"\" || o == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tgs.AddEdge(i, s, o, q.Predicate)\n\t\tif err := gs.Flush(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc shouldInline(v quad.Value) bool {\n\tswitch v.(type) {\n\tcase quad.Bool, quad.Int, quad.Float:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (s *GraphStreamHandler) serveNodesWithProps(ctx context.Context, gs *GraphStream, limit int) {\n\tpropsPath := path.NewPath(s.QS).Has(iriInlinePred, quad.Bool(true))\n\n\t// list of predicates marked as inline properties for gephi\n\tinline := make(map[quad.Value]struct{})\n\terr := propsPath.Iterate(ctx).EachValue(s.QS, func(v quad.Value) error {\n\t\tinline[v] = struct{}{}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tclog.Errorf(\"cannot iterate over properties: %v\", err)\n\t\treturn\n\t}\n\t// inline some well-known predicates\n\tfor _, iri := range defaultInline {\n\t\tinline[iri] = struct{}{}\n\t\tinline[iri.Full()] = struct{}{}\n\t}\n\n\tignore := make(map[quad.Value]struct{})\n\n\tnodes := iterator.NewNot(propsPath.BuildIterator(ctx), s.QS.NodesAllIterator())\n\n\tictx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\titc := iterator.Iterate(ictx, nodes).On(s.QS).Limit(limit)\n\n\tqi := 0\n\t_ = itc.EachValuePair(s.QS, func(v graph.Ref, nv quad.Value) error {\n\t\tif _, skip := ignore[nv]; skip {\n\t\t\treturn nil\n\t\t}\n\t\t// list of inline properties\n\t\tprops := make(map[quad.Value]quad.Value)\n\n\t\tvar (\n\t\t\tsid   string\n\t\t\th, oh valHash\n\t\t)\n\t\tquad.HashTo(nv, h[:])\n\n\t\tpredIt := s.QS.QuadIterator(quad.Subject, v).Iterate()\n\t\tdefer predIt.Close()\n\t\tfor predIt.Next(ictx) {\n\t\t\t// this check helps us ignore nodes with no links\n\t\t\tif sid == \"\" {\n\t\t\t\tsid = gs.addNode(nv, h, props)\n\t\t\t}\n\t\t\tq, err := s.QS.Quad(predIt.Result())\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := inline[q.Predicate]; ok {\n\t\t\t\tprops[q.Predicate] = q.Object\n\t\t\t\tignore[q.Object] = struct{}{}\n\t\t\t} else if shouldInline(q.Object) {\n\t\t\t\tprops[q.Predicate] = q.Object\n\t\t\t} else {\n\t\t\t\tquad.HashTo(q.Object, oh[:])\n\t\t\t\to := gs.addNode(q.Object, oh, nil)\n\t\t\t\tif o == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tgs.AddEdge(qi, sid, o, q.Predicate)\n\t\t\t\tqi++\n\t\t\t\tif err := gs.Flush(); err != nil {\n\t\t\t\t\tcancel()\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif err := predIt.Err(); err != nil {\n\t\t\tcancel()\n\t\t\treturn nil\n\t\t} else if sid == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\tif len(props) != 0 {\n\t\t\tgs.ChangeNode(nv, sid, props)\n\t\t}\n\t\tif err := gs.Flush(); err != nil {\n\t\t\tcancel()\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc valuesFromString(s string) []quad.Value {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\tarr := strings.Split(s, \",\")\n\tout := make([]quad.Value, 0, len(arr))\n\tfor _, s := range arr {\n\t\tout = append(out, quad.StringToValue(s))\n\t}\n\treturn out\n}\n\nfunc (s *GraphStreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, params httprouter.Params) {\n\tctx := context.TODO()\n\tvar limit int\n\tif s := r.FormValue(\"limit\"); s != \"\" {\n\t\tlimit, _ = strconv.Atoi(s)\n\t}\n\tif limit == 0 {\n\t\tlimit = defaultLimit\n\t}\n\tmode := \"raw\"\n\tif s := r.FormValue(\"mode\"); s != \"\" {\n\t\tmode = s\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/stream+json\")\n\tgs := NewGraphStream(w)\n\tswitch mode {\n\tcase \"nodes\":\n\t\ts.serveNodesWithProps(ctx, gs, limit)\n\tcase \"raw\":\n\t\tvalues := shape.FilterQuads(\n\t\t\tvaluesFromString(r.FormValue(\"sub\")),\n\t\t\tvaluesFromString(r.FormValue(\"pred\")),\n\t\t\tvaluesFromString(r.FormValue(\"obj\")),\n\t\t\tvaluesFromString(r.FormValue(\"label\")),\n\t\t)\n\t\ts.serveRawQuads(ctx, gs, values, limit)\n\tdefault:\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "internal/gephi/stream_test.go",
    "content": "package gephi\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestStreamEncoder(t *testing.T) {\n\tbuf := bytes.NewBuffer(nil)\n\tgs := NewGraphStream(buf)\n\tp := map[quad.Value]quad.Value{iriPosX: quad.Float(0), iriPosY: quad.Float(0)}\n\tgs.AddNode(quad.String(\"aaa\"), p)\n\tgs.AddNode(quad.String(\"bbb\"), p)\n\tgs.Flush()\n\tconst expect = \"{\\\"an\\\":{\\\"0\\\":{\\\"label\\\":\\\"aaa\\\",\\\"size\\\":20,\\\"x\\\":0,\\\"y\\\":0}}}\\r\\n{\\\"an\\\":{\\\"1\\\":{\\\"label\\\":\\\"bbb\\\",\\\"size\\\":20,\\\"x\\\":0,\\\"y\\\":0}}}\\r\\n\"\n\trequire.Equal(t, expect, buf.String())\n}\n"
  },
  {
    "path": "internal/http/api_v1.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\tcayleyhttp \"github.com/cayleygraph/cayley/server/http\"\n\t\"github.com/julienschmidt/httprouter\"\n)\n\ntype API struct {\n\tconfig *Config\n\thandle *graph.Handle\n}\n\nfunc (api *API) GetHandleForRequest(r *http.Request) (*graph.Handle, error) {\n\treturn cayleyhttp.HandleForRequest(api.handle, \"single\", nil, r)\n}\n\nfunc (api *API) RWOnly(handler httprouter.Handle) httprouter.Handle {\n\tif api.config.ReadOnly {\n\t\treturn func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {\n\t\t\tjsonResponse(w, http.StatusForbidden, \"Database is read-only.\")\n\t\t}\n\t}\n\treturn handler\n}\n\nfunc (api *API) APIv1(r *httprouter.Router) {\n\tr.POST(\"/query/:query_lang\", api.ServeV1Query)\n\tr.POST(\"/shape/:query_lang\", api.ServeV1Shape)\n\tr.POST(\"/write\", api.RWOnly(api.ServeV1Write))\n\tr.POST(\"/write/file/nquad\", api.RWOnly(api.ServeV1WriteNQuad))\n\tr.POST(\"/delete\", api.RWOnly(api.ServeV1Delete))\n}\n"
  },
  {
    "path": "internal/http/cors.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n)\n\n// CORS adds CORS related headers to responses\nfunc CORS(h http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t\tif origin := req.Header.Get(\"Origin\"); origin != \"\" {\n\t\t\tw.Header().Set(\"Access-Control-Allow-Origin\", origin)\n\t\t\tw.Header().Set(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS, PUT, DELETE\")\n\t\t\tw.Header().Set(\"Access-Control-Allow-Headers\",\n\t\t\t\t\"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization\")\n\t\t}\n\t\th.ServeHTTP(w, req)\n\t})\n}\n\n// HandlePreflight is an http.Handler for CORS Prelight requests\nfunc HandlePreflight(w http.ResponseWriter, r *http.Request) {\n\t// Adjust status code to 204\n\tw.WriteHeader(http.StatusNoContent)\n}\n"
  },
  {
    "path": "internal/http/health.go",
    "content": "package http\n\nimport \"net/http\"\n\n// HandleHealth is a route for handling health checks to the server\nfunc HandleHealth(w http.ResponseWriter, r *http.Request) {\n\t// Adjust status code to 204\n\tw.WriteHeader(http.StatusNoContent)\n}\n"
  },
  {
    "path": "internal/http/http.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage http\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/internal/gephi\"\n\tcayleyhttp \"github.com/cayleygraph/cayley/server/http\"\n\t\"github.com/cayleygraph/cayley/ui\"\n)\n\nfunc jsonResponse(w http.ResponseWriter, code int, err interface{}) {\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(code)\n\tw.Write([]byte(`{\"error\": `))\n\tdata, _ := json.Marshal(fmt.Sprint(err))\n\tw.Write(data)\n\tw.Write([]byte(`}`))\n}\n\n// Config holds the HTTP server configuration\ntype Config struct {\n\tReadOnly bool\n\tTimeout  time.Duration\n\tBatch    int\n}\n\nfunc SetupRoutes(handle *graph.Handle, cfg *Config) error {\n\tui, err := fs.Sub(ui.FS, \"web\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tr := httprouter.New()\n\n\t// Health check\n\tr.HandlerFunc(\"GET\", \"/health\", HandleHealth)\n\n\t// Handle CORS preflight request\n\tr.HandlerFunc(\"OPTIONS\", \"/*path\", HandlePreflight)\n\n\t// Register API V1\n\tapi := &API{config: cfg, handle: handle}\n\tapi.APIv1(r)\n\n\t// Register Gephi API\n\tgs := &gephi.GraphStreamHandler{QS: handle.QuadStore}\n\tr.GET(\"/gephi/gs\", gs.ServeHTTP)\n\n\t// Register API V2\n\tapi2 := cayleyhttp.NewBoundAPIv2(handle, r)\n\tapi2.SetReadOnly(cfg.ReadOnly)\n\tapi2.SetBatchSize(cfg.Batch)\n\tapi2.SetQueryTimeout(cfg.Timeout)\n\n\t// For non API requests serve the UI\n\tr.NotFound = http.FileServer(http.FS(ui))\n\n\thttp.Handle(\"/\", CORS(LogRequest(r)))\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/http/http_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage http\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/quad\"\n)\n\nvar parseTests = []struct {\n\tmessage string\n\tinput   string\n\texpect  []quad.Quad\n\terr     error\n}{\n\t{\n\t\tmessage: \"parse correct JSON\",\n\t\tinput: `[\n\t\t\t{\"subject\": \"foo\", \"predicate\": \"bar\", \"object\": \"baz\"},\n\t\t\t{\"subject\": \"foo\", \"predicate\": \"bar\", \"object\": \"baz\", \"label\": \"graph\"}\n\t\t]`,\n\t\texpect: []quad.Quad{\n\t\t\tquad.Make(\"foo\", \"bar\", \"baz\", nil),\n\t\t\tquad.Make(\"foo\", \"bar\", \"baz\", \"graph\"),\n\t\t},\n\t\terr: nil,\n\t},\n\t{\n\t\tmessage: \"parse correct JSON with extra field\",\n\t\tinput: `[\n\t\t\t{\"subject\": \"foo\", \"predicate\": \"bar\", \"object\": \"foo\", \"something_else\": \"extra data\"}\n\t\t]`,\n\t\texpect: []quad.Quad{\n\t\t\tquad.Make(\"foo\", \"bar\", \"foo\", nil),\n\t\t},\n\t\terr: nil,\n\t},\n\t{\n\t\tmessage: \"reject incorrect JSON\",\n\t\tinput: `[\n\t\t\t{\"subject\": \"foo\", \"predicate\": \"bar\"}\n\t\t]`,\n\t\texpect: nil,\n\t\terr:    fmt.Errorf(\"invalid quad at index %d. %v\", 0, quad.Make(\"foo\", \"bar\", nil, nil)),\n\t},\n}\n\nfunc TestParseJSON(t *testing.T) {\n\tfor _, test := range parseTests {\n\t\tgot, err := ParseJSONToQuadList([]byte(test.input))\n\t\tif fmt.Sprint(err) != fmt.Sprint(test.err) {\n\t\t\tt.Errorf(\"Failed to %v with unexpected error, got:%v expected %v\", test.message, err, test.err)\n\t\t}\n\t\tif !reflect.DeepEqual(got, test.expect) {\n\t\t\tt.Errorf(\"Failed to %v, got:%v expect:%v\", test.message, got, test.expect)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/http/logs.go",
    "content": "package http\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n)\n\n// statusWriter wraps http.ResponseWriter and captures the written status code\ntype statusWriter struct {\n\thttp.ResponseWriter\n\tcode int\n}\n\n// newStatusWriter returns an initialized statusWriter\nfunc newStatusWriter(w http.ResponseWriter) *statusWriter {\n\treturn &statusWriter{w, 200}\n}\n\n// WriteHeader wraps ResponseWriter WriteHeader and saves the code\nfunc (w *statusWriter) WriteHeader(code int) {\n\tw.ResponseWriter.WriteHeader(code)\n\tw.code = code\n}\n\n// getAddress returns the address of the incoming request\nfunc getAddress(req *http.Request) string {\n\taddr := req.Header.Get(\"X-Real-IP\")\n\tif addr == \"\" {\n\t\taddr = req.Header.Get(\"X-Forwarded-For\")\n\t\tif addr == \"\" {\n\t\t\taddr = req.RemoteAddr\n\t\t}\n\t}\n\treturn addr\n}\n\n// LogRequest wraps a http.Handler and emits logs about the request and the response\nfunc LogRequest(handler http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t\tstart := time.Now()\n\t\taddr := getAddress(req)\n\t\tsw := newStatusWriter(w)\n\t\tclog.Infof(\"started %s %s for %s\", req.Method, req.URL.Path, addr)\n\t\thandler.ServeHTTP(sw, req)\n\t\tclog.Infof(\"completed %v %s %s in %v\", sw.code, http.StatusText(sw.code), req.URL.Path, time.Since(start))\n\t})\n}\n"
  },
  {
    "path": "internal/http/query.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage http\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\n\t\"github.com/cayleygraph/cayley/query\"\n)\n\ntype SuccessQueryWrapper struct {\n\tResult interface{} `json:\"result\"`\n}\n\ntype ErrorQueryWrapper struct {\n\tError string `json:\"error\"`\n}\n\nfunc WriteError(w io.Writer, err error) error {\n\tenc := json.NewEncoder(w)\n\t//enc.SetIndent(\"\", \" \")\n\treturn enc.Encode(ErrorQueryWrapper{err.Error()})\n}\n\nfunc WriteResult(w io.Writer, result interface{}) error {\n\tenc := json.NewEncoder(w)\n\t//enc.SetIndent(\"\", \" \")\n\treturn enc.Encode(SuccessQueryWrapper{result})\n}\n\nfunc (api *API) contextForRequest(r *http.Request) (context.Context, func()) {\n\tctx := context.TODO() // TODO(dennwc): get from request\n\tcancel := func() {}\n\tif api.config.Timeout > 0 {\n\t\tctx, cancel = context.WithTimeout(ctx, api.config.Timeout)\n\t}\n\treturn ctx, cancel\n}\n\nfunc defaultErrorFunc(w query.ResponseWriter, err error) {\n\tdata, _ := json.Marshal(err.Error())\n\tw.WriteHeader(http.StatusBadRequest)\n\tw.Write([]byte(`{\"error\" : `))\n\tw.Write(data)\n\tw.Write([]byte(`}`))\n}\n\n// TODO(barakmich): Turn this into proper middleware.\n\n// ServeV1Query is the HTTP handler for queries in API V1\nfunc (api *API) ServeV1Query(w http.ResponseWriter, r *http.Request, params httprouter.Params) {\n\tctx, cancel := api.contextForRequest(r)\n\tdefer cancel()\n\tl := query.GetLanguage(params.ByName(\"query_lang\"))\n\tif l == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, \"Unknown query language.\")\n\t\treturn\n\t}\n\terrFunc := defaultErrorFunc\n\tif l.HTTPError != nil {\n\t\terrFunc = l.HTTPError\n\t}\n\tselect {\n\tcase <-ctx.Done():\n\t\terrFunc(w, ctx.Err())\n\t\treturn\n\tdefault:\n\t}\n\th, err := api.GetHandleForRequest(r)\n\tif err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tif l.HTTPQuery != nil {\n\t\tdefer r.Body.Close()\n\t\tl.HTTPQuery(ctx, h.QuadStore, w, r.Body)\n\t\treturn\n\t}\n\tif l.Session == nil {\n\t\terrFunc(w, errors.New(\"no support for HTTP interface for this query language\"))\n\t\treturn\n\t}\n\n\tpar, _ := url.ParseQuery(r.URL.RawQuery)\n\tlimit, _ := strconv.Atoi(par.Get(\"limit\"))\n\tif limit == 0 {\n\t\tlimit = 100\n\t}\n\n\tses := l.Session(h.QuadStore)\n\tbodyBytes, err := ioutil.ReadAll(r.Body)\n\tif err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tit, err := ses.Execute(ctx, string(bodyBytes), query.Options{\n\t\tCollation: query.JSON,\n\t\tLimit:     limit,\n\t})\n\tif err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tdefer it.Close()\n\n\tvar out []interface{}\n\tfor it.Next(ctx) {\n\t\tout = append(out, it.Result())\n\t}\n\tif err = it.Err(); err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\t_ = WriteResult(w, out)\n}\n\nfunc (api *API) ServeV1Shape(w http.ResponseWriter, r *http.Request, params httprouter.Params) {\n\tjsonResponse(w, http.StatusNotImplemented, \"Query shape API v1 is deprecated.\")\n}\n"
  },
  {
    "path": "internal/http/write.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage http\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strconv\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/internal/decompressor\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/nquads\"\n)\n\nfunc ParseJSONToQuadList(jsonBody []byte) (out []quad.Quad, _ error) {\n\tvar quads []struct {\n\t\tSubject   string `json:\"subject\"`\n\t\tPredicate string `json:\"predicate\"`\n\t\tObject    string `json:\"object\"`\n\t\tLabel     string `json:\"label\"`\n\t}\n\terr := json.Unmarshal(jsonBody, &quads)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tout = make([]quad.Quad, 0, len(quads))\n\tfor i, jq := range quads {\n\t\tq := quad.Quad{\n\t\t\tSubject:   quad.StringToValue(jq.Subject),\n\t\t\tPredicate: quad.StringToValue(jq.Predicate),\n\t\t\tObject:    quad.StringToValue(jq.Object),\n\t\t\tLabel:     quad.StringToValue(jq.Label),\n\t\t}\n\t\tif !q.IsValid() {\n\t\t\treturn nil, fmt.Errorf(\"invalid quad at index %d. %s\", i, q)\n\t\t}\n\t\tout = append(out, q)\n\t}\n\treturn out, nil\n}\n\nconst maxQuerySize = 1024 * 1024 // 1 MB\nfunc readLimit(r io.Reader) ([]byte, error) {\n\tlr := io.LimitReader(r, maxQuerySize).(*io.LimitedReader)\n\tdata, err := ioutil.ReadAll(lr)\n\tif err != nil && lr.N <= 0 {\n\t\terr = errors.New(\"request is too large\")\n\t}\n\treturn data, err\n}\n\nfunc (api *API) ServeV1Write(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {\n\tif api.config.ReadOnly {\n\t\tjsonResponse(w, 400, \"Database is read-only.\")\n\t\treturn\n\t}\n\t// TODO: streaming reader\n\tbodyBytes, err := readLimit(r.Body)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tquads, err := ParseJSONToQuadList(bodyBytes)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\th, err := api.GetHandleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tif err = h.QuadWriter.AddQuadSet(quads); err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"{\\\"result\\\": \\\"Successfully wrote %d quads.\\\"}\", len(quads))\n}\n\nfunc (api *API) ServeV1WriteNQuad(w http.ResponseWriter, r *http.Request, params httprouter.Params) {\n\tif api.config.ReadOnly {\n\t\tjsonResponse(w, 400, \"Database is read-only.\")\n\t\treturn\n\t}\n\n\tformFile, _, err := r.FormFile(\"NQuadFile\")\n\tif err != nil {\n\t\tclog.Errorf(\"%v\", err)\n\t\tjsonResponse(w, 500, \"Couldn't read file: \"+err.Error())\n\t\treturn\n\t}\n\tdefer formFile.Close()\n\n\tblockSize, blockErr := strconv.Atoi(r.URL.Query().Get(\"block_size\"))\n\tif blockErr != nil {\n\t\tblockSize = quad.DefaultBatch\n\t}\n\n\tquadReader, err := decompressor.New(formFile)\n\t// TODO(kortschak) Make this configurable from the web UI.\n\tdec := nquads.NewReader(quadReader, false)\n\n\th, err := api.GetHandleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tqw := graph.NewWriter(h.QuadWriter)\n\tn, err := quad.CopyBatch(qw, dec, blockSize)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\terr = qw.Close()\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"{\\\"result\\\": \\\"Successfully wrote %d quads.\\\"}\", n)\n}\n\nfunc (api *API) ServeV1Delete(w http.ResponseWriter, r *http.Request, params httprouter.Params) {\n\tif api.config.ReadOnly {\n\t\tjsonResponse(w, 400, \"Database is read-only.\")\n\t\treturn\n\t}\n\tbodyBytes, err := readLimit(r.Body)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tquads, err := ParseJSONToQuadList(bodyBytes)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\th, err := api.GetHandleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, 400, err)\n\t\treturn\n\t}\n\tfor _, q := range quads {\n\t\terr = h.QuadWriter.RemoveQuad(q)\n\t\tif err != nil && !graph.IsQuadNotExist(err) {\n\t\t\tjsonResponse(w, 400, err)\n\t\t\treturn\n\t\t}\n\t}\n\tfmt.Fprintf(w, \"{\\\"result\\\": \\\"Successfully deleted %d quads.\\\"}\", len(quads))\n}\n"
  },
  {
    "path": "internal/linkedql/schema/schema.go",
    "content": "package schema\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"strconv\"\n\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t// Steps are imported here so they be registered and documented in the schema\n\t_ \"github.com/cayleygraph/cayley/query/linkedql/steps\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/owl\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n\t\"github.com/cayleygraph/quad/voc/rdfs\"\n\t\"github.com/cayleygraph/quad/voc/xsd\"\n)\n\n// rdfgGraph is the W3C type for named graphs\nconst (\n\trdfgNamespace = \"http://www.w3.org/2004/03/trix/rdfg-1/\"\n\trdfgPrefix    = \"rdfg:\"\n\trdfgGraph     = rdfgPrefix + \"Graph\"\n)\n\nvar (\n\tpathStep         = reflect.TypeOf((*linkedql.PathStep)(nil)).Elem()\n\titeratorStep     = reflect.TypeOf((*linkedql.IteratorStep)(nil)).Elem()\n\tentityIdentifier = reflect.TypeOf((*linkedql.EntityIdentifier)(nil)).Elem()\n\tvalue            = reflect.TypeOf((*quad.Value)(nil)).Elem()\n\tpropertyPath     = reflect.TypeOf((*linkedql.PropertyPath)(nil))\n\tstringMap        = reflect.TypeOf(map[string]string{})\n\tgraphPattern     = reflect.TypeOf(linkedql.GraphPattern(nil))\n)\n\nfunc typeToRange(t reflect.Type) string {\n\tif t == stringMap {\n\t\treturn \"rdf:JSON\"\n\t}\n\tif t == graphPattern {\n\t\treturn rdfgGraph\n\t}\n\tif t.Kind() == reflect.Slice {\n\t\treturn typeToRange(t.Elem())\n\t}\n\tif t.Kind() == reflect.String {\n\t\treturn xsd.String\n\t}\n\tif t.Kind() == reflect.Bool {\n\t\treturn xsd.Boolean\n\t}\n\tif kind := t.Kind(); kind == reflect.Int64 || kind == reflect.Int {\n\t\treturn xsd.Int\n\t}\n\tif t.Implements(value) {\n\t\treturn rdfs.Resource\n\t}\n\tif t == entityIdentifier {\n\t\treturn owl.Thing\n\t}\n\tif t == pathStep {\n\t\treturn linkedql.Prefix + \"PathStep\"\n\t}\n\tif t == propertyPath {\n\t\treturn linkedql.Prefix + \"PropertyPath\"\n\t}\n\tpanic(\"Unexpected type \" + t.String())\n}\n\n// identified is used for referencing a type\ntype identified struct {\n\tID string `json:\"@id\"`\n}\n\n// newIdentified creates new identified struct\nfunc newIdentified(id string) identified {\n\treturn identified{ID: id}\n}\n\n// cardinalityRestriction is used to indicate a how many values can a property get\ntype cardinalityRestriction struct {\n\tID          string     `json:\"@id\"`\n\tType        string     `json:\"@type\"`\n\tCardinality int        `json:\"owl:cardinality\"`\n\tProperty    identified `json:\"owl:onProperty\"`\n}\n\nfunc newBlankNodeID() string {\n\treturn quad.RandomBlankNode().String()\n}\n\n// newSingleCardinalityRestriction creates a cardinality of 1 restriction for given property\nfunc newSingleCardinalityRestriction(prop string) cardinalityRestriction {\n\treturn cardinalityRestriction{\n\t\tID:          newBlankNodeID(),\n\t\tType:        owl.Restriction,\n\t\tCardinality: 1,\n\t\tProperty:    identified{ID: prop},\n\t}\n}\n\ntype owlPropertyRestriction struct {\n\tID       string     `json:\"@id\"`\n\tType     string     `json:\"@type\"`\n\tProperty identified `json:\"owl:onProperty\"`\n}\n\nfunc newOWLPropertyRestriction(prop string) owlPropertyRestriction {\n\treturn owlPropertyRestriction{\n\t\tID:       newBlankNodeID(),\n\t\tType:     owl.Restriction,\n\t\tProperty: identified{ID: prop},\n\t}\n}\n\n// minCardinalityRestriction is used to indicate a how many values can a property get at the very least\ntype minCardinalityRestriction struct {\n\towlPropertyRestriction\n\tMinCardinality int `json:\"owl:minCardinality\"`\n}\n\n// maxCardinalityRestriction is used to indicate a how many values can a property get at most\ntype maxCardinalityRestriction struct {\n\towlPropertyRestriction\n\tMaxCardinality int `json:\"owl:maxCardinality\"`\n}\n\nfunc newMinCardinalityRestriction(prop string, minCardinality int) minCardinalityRestriction {\n\treturn minCardinalityRestriction{\n\t\towlPropertyRestriction: newOWLPropertyRestriction(prop),\n\t\tMinCardinality:         minCardinality,\n\t}\n}\n\nfunc newSingleMaxCardinalityRestriction(prop string) maxCardinalityRestriction {\n\treturn maxCardinalityRestriction{\n\t\towlPropertyRestriction: newOWLPropertyRestriction(prop),\n\t\tMaxCardinality:         1,\n\t}\n}\n\n// getOWLPropertyType for given kind of value type returns property OWL type\nfunc getOWLPropertyType(kind reflect.Kind) string {\n\tif kind == reflect.String || kind == reflect.Bool || kind == reflect.Int64 || kind == reflect.Int {\n\t\treturn owl.DatatypeProperty\n\t}\n\treturn owl.ObjectProperty\n}\n\n// property is used to declare a property\ntype property struct {\n\tID     string      `json:\"@id\"`\n\tType   string      `json:\"@type\"`\n\tDomain interface{} `json:\"rdfs:domain\"`\n\tRange  interface{} `json:\"rdfs:range\"`\n}\n\n// class is used to declare a class\ntype class struct {\n\tID           string        `json:\"@id\"`\n\tType         string        `json:\"@type\"`\n\tComment      string        `json:\"rdfs:comment\"`\n\tSuperClasses []interface{} `json:\"rdfs:subClassOf\"`\n}\n\n// newClass creates a new class struct\nfunc newClass(id string, superClasses []interface{}, comment string) class {\n\treturn class{\n\t\tID:           id,\n\t\tType:         rdfs.Class,\n\t\tSuperClasses: superClasses,\n\t\tComment:      comment,\n\t}\n}\n\n// getStepTypeClasses for given step type returns the matching class identifiers\nfunc getStepTypeClasses(t reflect.Type) []string {\n\tvar typeClasses []string\n\tif t.Implements(pathStep) {\n\t\ttypeClasses = append(typeClasses, linkedql.Prefix+\"PathStep\")\n\t}\n\tif t.Implements(iteratorStep) {\n\t\ttypeClasses = append(typeClasses, linkedql.Prefix+\"IteratorStep\")\n\t}\n\treturn typeClasses\n}\n\ntype list struct {\n\tMembers []interface{} `json:\"@list\"`\n}\n\nfunc newList(members []interface{}) list {\n\treturn list{\n\t\tMembers: members,\n\t}\n}\n\ntype unionOf struct {\n\tID   string `json:\"@id\"`\n\tType string `json:\"@type\"`\n\tList list   `json:\"owl:unionOf\"`\n}\n\nfunc newUnionOf(classes []string) unionOf {\n\tvar members []interface{}\n\tfor _, class := range classes {\n\t\tmembers = append(members, newIdentified(class))\n\t}\n\treturn unionOf{\n\t\tID:   newBlankNodeID(),\n\t\tType: owl.Class,\n\t\tList: newList(members),\n\t}\n}\n\nfunc newGenerator() *generator {\n\treturn &generator{\n\t\tpropToTypes:   make(map[string]map[string]struct{}),\n\t\tpropToDomains: make(map[string]map[string]struct{}),\n\t\tpropToRanges:  make(map[string]map[string]struct{}),\n\t}\n}\n\ntype generator struct {\n\tout           []interface{}\n\tpropToTypes   map[string]map[string]struct{}\n\tpropToDomains map[string]map[string]struct{}\n\tpropToRanges  map[string]map[string]struct{}\n}\n\n// returns super types\nfunc (g *generator) addTypeFields(name string, t reflect.Type, indirect bool) []interface{} {\n\tvar super []interface{}\n\tfor j := 0; j < t.NumField(); j++ {\n\t\tf := t.Field(j)\n\t\tif f.Anonymous {\n\t\t\tif f.Type.Kind() != reflect.Struct || !indirect {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsuper = append(super, g.addTypeFields(name, f.Type, false)...)\n\t\t\tcontinue\n\t\t}\n\t\tprop := linkedql.Prefix + f.Tag.Get(\"json\")\n\t\tvar hasMinCardinality bool\n\t\tv, ok := f.Tag.Lookup(\"minCardinality\")\n\t\tif ok {\n\t\t\tminCardinality, err := strconv.Atoi(v)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\thasMinCardinality = true\n\t\t\tsuper = append(super, newMinCardinalityRestriction(prop, minCardinality))\n\t\t}\n\t\tif f.Type.Kind() != reflect.Slice {\n\t\t\tif hasMinCardinality {\n\t\t\t\tsuper = append(super, newSingleMaxCardinalityRestriction(prop))\n\t\t\t} else {\n\t\t\t\tsuper = append(super, newSingleCardinalityRestriction(prop))\n\t\t\t}\n\t\t}\n\t\ttyp := getOWLPropertyType(f.Type.Kind())\n\n\t\tif g.propToTypes[prop] == nil {\n\t\t\tg.propToTypes[prop] = make(map[string]struct{})\n\t\t}\n\t\tg.propToTypes[prop][typ] = struct{}{}\n\n\t\tif g.propToDomains[prop] == nil {\n\t\t\tg.propToDomains[prop] = make(map[string]struct{})\n\t\t}\n\t\tg.propToDomains[prop][name] = struct{}{}\n\n\t\tif g.propToRanges[prop] == nil {\n\t\t\tg.propToRanges[prop] = make(map[string]struct{})\n\t\t}\n\t\tg.propToRanges[prop][typeToRange(f.Type)] = struct{}{}\n\t}\n\treturn super\n}\n\nfunc (g *generator) AddType(name string, t reflect.Type) {\n\tstep, ok := reflect.New(t).Interface().(linkedql.Step)\n\tif !ok {\n\t\treturn\n\t}\n\tvar super []interface{}\n\tstepTypeClasses := getStepTypeClasses(reflect.PtrTo(t))\n\tfor _, typeClass := range stepTypeClasses {\n\t\tsuper = append(super, newIdentified(typeClass))\n\t}\n\tsuper = append(super, g.addTypeFields(name, t, true)...)\n\tg.out = append(g.out, newClass(name, super, step.Description()))\n}\n\nfunc (g *generator) Generate() []byte {\n\tfor prop, types := range g.propToTypes {\n\t\tif len(types) != 1 {\n\t\t\tpanic(\"Properties must be either object properties or datatype properties. \" + prop + \" has both.\")\n\t\t}\n\t\tvar typ string\n\t\tfor t := range types {\n\t\t\ttyp = t\n\t\t\tbreak\n\t\t}\n\t\tvar domains []string\n\t\tfor d := range g.propToDomains[prop] {\n\t\t\tdomains = append(domains, d)\n\t\t}\n\t\tvar ranges []string\n\t\tfor r := range g.propToRanges[prop] {\n\t\t\tranges = append(ranges, r)\n\t\t}\n\t\tvar dom interface{}\n\t\tif len(domains) == 1 {\n\t\t\tdom = identified{domains[0]}\n\t\t} else {\n\t\t\tdom = newUnionOf(domains)\n\t\t}\n\t\tvar rng interface{}\n\t\tif len(ranges) == 1 {\n\t\t\trng = newIdentified(ranges[0])\n\t\t} else {\n\t\t\trng = newUnionOf(ranges)\n\t\t}\n\t\tg.out = append(g.out, property{\n\t\t\tID:     prop,\n\t\t\tType:   typ,\n\t\t\tDomain: dom,\n\t\t\tRange:  rng,\n\t\t})\n\t}\n\tgraph := []interface{}{\n\t\tmap[string]string{\n\t\t\t\"@id\":   linkedql.Prefix + \"Step\",\n\t\t\t\"@type\": owl.Class,\n\t\t},\n\t\tmap[string]interface{}{\n\t\t\t\"@id\":           linkedql.Prefix + \"PathStep\",\n\t\t\t\"@type\":         owl.Class,\n\t\t\trdfs.SubClassOf: identified{ID: linkedql.Prefix + \"Step\"},\n\t\t},\n\t\tmap[string]interface{}{\n\t\t\t\"@id\":           linkedql.Prefix + \"IteratorStep\",\n\t\t\t\"@type\":         owl.Class,\n\t\t\trdfs.SubClassOf: identified{ID: linkedql.Prefix + \"Step\"},\n\t\t},\n\t}\n\tgraph = append(graph, g.out...)\n\tdata, err := json.Marshal(map[string]interface{}{\n\t\t\"@context\": map[string]interface{}{\n\t\t\t\"rdf\":      rdf.NS,\n\t\t\t\"rdfs\":     rdfs.NS,\n\t\t\t\"owl\":      owl.NS,\n\t\t\t\"xsd\":      xsd.NS,\n\t\t\t\"linkedql\": linkedql.Namespace,\n\t\t\t\"rdfg\":     rdfgNamespace,\n\t\t},\n\t\t\"@graph\": graph,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn data\n}\n\n// Generate a schema in JSON-LD format that contains all registered LinkedQL types and properties.\nfunc Generate() []byte {\n\tg := newGenerator()\n\tfor _, name := range linkedql.RegisteredTypes() {\n\t\tt, ok := linkedql.TypeByName(name)\n\t\tif !ok {\n\t\t\tpanic(\"type is registered, but the lookup fails\")\n\t\t}\n\t\tg.AddType(name, t)\n\t}\n\treturn g.Generate()\n}\n"
  },
  {
    "path": "internal/linkedql/schema/schema_test.go",
    "content": "package schema\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n)\n\nfunc TestMarshalSchema(t *testing.T) {\n\tout := Generate()\n\tvar o interface{}\n\terr := json.Unmarshal(out, &o)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "internal/load.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/internal/decompressor\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/nquads\"\n)\n\n// Load loads a graph from the given path and write it to qw.  See\n// DecompressAndLoad for more information.\nfunc Load(qw quad.WriteCloser, batch int, path, typ string) error {\n\treturn DecompressAndLoad(qw, batch, path, typ)\n}\n\ntype readCloser struct {\n\tquad.ReadCloser\n\tclose func() error\n}\n\nfunc (r readCloser) Close() error {\n\terr := r.ReadCloser.Close()\n\tif r.close != nil {\n\t\tr.close()\n\t}\n\treturn err\n}\n\ntype nopCloser struct {\n\tquad.Reader\n}\n\nfunc (r nopCloser) Close() error { return nil }\n\nfunc QuadReaderFor(path, typ string) (quad.ReadCloser, error) {\n\tvar (\n\t\tr io.Reader\n\t\tc io.Closer\n\t)\n\tif path == \"-\" {\n\t\tr = os.Stdin\n\t} else if u, err := url.Parse(path); err != nil || u.Scheme == \"file\" || u.Scheme == \"\" {\n\t\t// Don't alter relative URL path or non-URL path parameter.\n\t\tif u.Scheme != \"\" && err == nil {\n\t\t\t// Recovery heuristic for mistyping \"file://path/to/file\".\n\t\t\tpath = filepath.Join(u.Host, u.Path)\n\t\t}\n\t\tf, err := os.Open(path)\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, err\n\t\t} else if err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not open file %q: %v\", path, err)\n\t\t}\n\t\tr, c = f, f\n\t} else {\n\t\tres, err := http.Get(path)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not get resource <%s>: %v\", u, err)\n\t\t}\n\t\t// TODO(dennwc): save content type for format auto-detection\n\t\tr, c = res.Body, res.Body\n\t}\n\n\tr, err := decompressor.New(r)\n\tif err != nil {\n\t\tif c != nil {\n\t\t\tc.Close()\n\t\t}\n\t\tif err == io.EOF {\n\t\t\treturn nopCloser{quad.NewReader(nil)}, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tvar qr quad.ReadCloser\n\tswitch typ {\n\tcase \"cquad\", \"nquad\": // legacy\n\t\tqr = nquads.NewReader(r, false)\n\tdefault:\n\t\tvar format *quad.Format\n\t\tif typ == \"\" {\n\t\t\tname := filepath.Base(path)\n\t\t\tname = strings.TrimSuffix(name, \".gz\")\n\t\t\tname = strings.TrimSuffix(name, \".bz2\")\n\t\t\tformat = quad.FormatByExt(filepath.Ext(name))\n\t\t\tif format == nil {\n\t\t\t\ttyp = \"nquads\"\n\t\t\t}\n\t\t}\n\t\tif format == nil {\n\t\t\tformat = quad.FormatByName(typ)\n\t\t}\n\t\tif format == nil {\n\t\t\terr = fmt.Errorf(\"unknown quad format %q\", typ)\n\t\t} else if format.Reader == nil {\n\t\t\terr = fmt.Errorf(\"decoding of %q is not supported\", typ)\n\t\t}\n\t\tif err != nil {\n\t\t\tif c != nil {\n\t\t\t\tc.Close()\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t\tqr = format.Reader(r)\n\t}\n\tif c != nil {\n\t\treturn readCloser{ReadCloser: qr, close: c.Close}, nil\n\t}\n\treturn qr, nil\n}\n\n// DecompressAndLoad will load or fetch a graph from the given path, decompress\n// it, and then call the given load function to process the decompressed graph.\n// If no loadFn is provided, db.Load is called.\nfunc DecompressAndLoad(qw quad.WriteCloser, batch int, path, typ string) error {\n\tif path == \"\" {\n\t\treturn nil\n\t}\n\tqr, err := QuadReaderFor(path, typ)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer qr.Close()\n\n\t_, err = quad.CopyBatch(&batchLogger{w: qw}, qr, batch)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: failed to load data: %v\", err)\n\t}\n\treturn qw.Close()\n}\n\ntype batchLogger struct {\n\tcnt int\n\tw   quad.Writer\n}\n\nfunc (w *batchLogger) WriteQuads(quads []quad.Quad) (int, error) {\n\tn, err := w.w.WriteQuads(quads)\n\tif clog.V(2) {\n\t\tw.cnt += n\n\t\tclog.Infof(\"Wrote %d quads.\", w.cnt)\n\t}\n\treturn n, err\n}\n"
  },
  {
    "path": "internal/lru/lru.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage lru\n\nimport (\n\t\"container/list\"\n\t\"sync\"\n)\n\n// TODO(kortschak) Reimplement without container/list.\n\n// Cache implements an LRU cache.\ntype Cache struct {\n\tmu       sync.Mutex\n\tcache    map[string]*list.Element\n\tpriority *list.List\n\tmaxSize  int\n}\n\ntype kv struct {\n\tkey   string\n\tvalue interface{}\n}\n\nfunc New(size int) *Cache {\n\treturn &Cache{\n\t\tmaxSize:  size,\n\t\tpriority: list.New(),\n\t\tcache:    make(map[string]*list.Element),\n\t}\n}\n\nfunc (lru *Cache) Put(key string, value interface{}) {\n\tif _, ok := lru.Get(key); ok {\n\t\treturn\n\t}\n\n\tlru.mu.Lock()\n\tdefer lru.mu.Unlock()\n\tif len(lru.cache) == lru.maxSize {\n\t\tlast := lru.priority.Remove(lru.priority.Back())\n\t\tdelete(lru.cache, last.(kv).key)\n\t}\n\tlru.priority.PushFront(kv{key: key, value: value})\n\tlru.cache[key] = lru.priority.Front()\n}\n\nfunc (lru *Cache) Del(key string) {\n\tlru.mu.Lock()\n\tdefer lru.mu.Unlock()\n\te := lru.cache[key]\n\tif e == nil {\n\t\treturn\n\t}\n\tdelete(lru.cache, key)\n\tlru.priority.Remove(e)\n}\n\nfunc (lru *Cache) Get(key string) (interface{}, bool) {\n\tlru.mu.Lock()\n\tdefer lru.mu.Unlock()\n\tif element, ok := lru.cache[key]; ok {\n\t\tlru.priority.MoveToFront(element)\n\t\treturn element.Value.(kv).value, true\n\t}\n\treturn nil, false\n}\n"
  },
  {
    "path": "internal/lru/lru_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage lru\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\n// checks fatal error: concurrent map read and map write\n\nfunc TestPanicLRUCache(t *testing.T) {\n\txch := make(chan int)\n\tc := New(1024)\n\tfor i := 0; i < 100; i++ {\n\t\tgo func(i int) {\n\t\t\tkey := fmt.Sprintf(\"Key%d\", i)\n\t\t\tc.Put(key, i)\n\t\t\tc.Get(key)\n\t\t\txch <- i\n\n\t\t}(i)\n\n\t}\n\tfor i := 0; i < 100; i++ {\n\t\t<-xch\n\t}\n\n}\n"
  },
  {
    "path": "internal/repl/repl.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// +build !appengine\n\npackage repl\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/peterh/liner\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/quad/nquads\"\n)\n\nfunc trace(s string) (string, time.Time) {\n\treturn s, time.Now()\n}\n\nfunc un(s string, startTime time.Time) {\n\tendTime := time.Now()\n\n\tfmt.Printf(s, float64(endTime.UnixNano()-startTime.UnixNano())/float64(1e6))\n}\n\nfunc Run(ctx context.Context, qu string, ses query.REPLSession) error {\n\tnResults := 0\n\tstartTrace, startTime := trace(\"Elapsed time: %g ms\\n\\n\")\n\tdefer func() {\n\t\tif nResults > 0 {\n\t\t\tun(startTrace, startTime)\n\t\t}\n\t}()\n\tfmt.Printf(\"\\n\")\n\tit, err := ses.Execute(ctx, qu, query.Options{\n\t\tCollation: query.REPL,\n\t\tLimit:     100,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer it.Close()\n\tfor it.Next(ctx) {\n\t\tfmt.Print(it.Result())\n\t\tnResults++\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn err\n\t}\n\tif nResults > 0 {\n\t\tresults := \"Result\"\n\t\tif nResults > 1 {\n\t\t\tresults += \"s\"\n\t\t}\n\t\tfmt.Printf(\"-----------\\n%d %s\\n\", nResults, results)\n\t}\n\treturn nil\n}\n\nconst (\n\tdefaultLanguage = \"gizmo\"\n\n\tps1 = \"cayley> \"\n\tps2 = \"...     \"\n\n\thistory = \".cayley_history\"\n)\n\nfunc Repl(ctx context.Context, h *graph.Handle, queryLanguage string, timeout time.Duration) error {\n\tif queryLanguage == \"\" {\n\t\tqueryLanguage = defaultLanguage\n\t}\n\tl := query.GetLanguage(queryLanguage)\n\tif l == nil || l.Session == nil {\n\t\treturn fmt.Errorf(\"unsupported query language: %q\", queryLanguage)\n\t}\n\tses := l.Session(h.QuadStore)\n\n\tterm, err := terminal(history)\n\tif os.IsNotExist(err) {\n\t\tfmt.Printf(\"creating new history file: %q\\n\", history)\n\t}\n\tdefer persist(term, history)\n\n\tvar (\n\t\tprompt = ps1\n\n\t\tcode string\n\t)\n\n\tnewCtx := func() (context.Context, func()) { return ctx, func() {} }\n\tif timeout > 0 {\n\t\tnewCtx = func() (context.Context, func()) { return context.WithTimeout(ctx, timeout) }\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t}\n\t\tif len(code) == 0 {\n\t\t\tprompt = ps1\n\t\t} else {\n\t\t\tprompt = ps2\n\t\t}\n\t\tline, err := term.Prompt(prompt)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tfmt.Println()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\tterm.AppendHistory(line)\n\n\t\tline = strings.TrimSpace(line)\n\t\tif len(line) == 0 || line[0] == '#' {\n\t\t\tcontinue\n\t\t}\n\n\t\tif code == \"\" {\n\t\t\tcmd, args := splitLine(line)\n\n\t\t\tswitch cmd {\n\t\t\tcase \":debug\":\n\t\t\t\targs = strings.TrimSpace(args)\n\t\t\t\tvar debug bool\n\t\t\t\tswitch args {\n\t\t\t\tcase \"t\":\n\t\t\t\t\tdebug = true\n\t\t\t\tcase \"f\":\n\t\t\t\t\t// Do nothing.\n\t\t\t\tdefault:\n\t\t\t\t\tdebug, err = strconv.ParseBool(args)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tfmt.Printf(\"Error: cannot parse %q as a valid boolean - acceptable values: 't'|'true' or 'f'|'false'\\n\", args)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif debug {\n\t\t\t\t\tclog.SetV(2)\n\t\t\t\t} else {\n\t\t\t\t\tclog.SetV(0)\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Debug set to %t\\n\", debug)\n\t\t\t\tcontinue\n\n\t\t\tcase \":a\":\n\t\t\t\tquad, err := nquads.Parse(args)\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = h.QuadWriter.AddQuad(quad)\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Printf(\"Error: not a valid quad: %v\\n\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcontinue\n\n\t\t\tcase \":d\":\n\t\t\t\tquad, err := nquads.Parse(args)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Printf(\"Error: not a valid quad: %v\\n\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\terr = h.QuadWriter.RemoveQuad(quad)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Printf(\"error deleting: %v\\n\", err)\n\t\t\t\t}\n\t\t\t\tcontinue\n\n\t\t\tcase \"help\":\n\t\t\t\tfmt.Printf(\"Help\\n\\texit // Exit\\n\\thelp // this help\\n\\td: <quad> // delete quad\\n\\ta: <quad> // add quad\\n\\t:debug [t|f]\\n\")\n\t\t\t\tcontinue\n\n\t\t\tcase \"exit\":\n\t\t\t\tterm.Close()\n\t\t\t\tos.Exit(0)\n\n\t\t\tdefault:\n\t\t\t\tif cmd[0] == ':' {\n\t\t\t\t\tfmt.Printf(\"Unknown command: %q\\n\", cmd)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcode += line\n\n\t\tnctx, cancel := newCtx()\n\t\terr = Run(nctx, code, ses)\n\t\tcancel()\n\t\tif err == query.ErrParseMore {\n\t\t\t// collect more input\n\t\t} else if err != nil {\n\t\t\tfmt.Println(\"Error: \", err)\n\t\t\tcode = \"\"\n\t\t} else {\n\t\t\tcode = \"\"\n\t\t}\n\t}\n}\n\n// Splits a line into a command and its arguments\n// e.g. \":a b c d .\" will be split into \":a\" and \" b c d .\"\nfunc splitLine(line string) (string, string) {\n\tvar command, arguments string\n\n\tline = strings.TrimSpace(line)\n\n\t// An empty line/a line consisting of whitespace contains neither command nor arguments\n\tif len(line) > 0 {\n\t\tcommand = strings.Fields(line)[0]\n\n\t\t// A line containing only a command has no arguments\n\t\tif len(line) > len(command) {\n\t\t\targuments = line[len(command):]\n\t\t}\n\t}\n\n\treturn command, arguments\n}\n\nfunc terminal(path string) (*liner.State, error) {\n\tterm := liner.NewLiner()\n\n\tgo func() {\n\t\tc := make(chan os.Signal, 1)\n\t\tsignal.Notify(c, os.Interrupt, os.Kill)\n\t\t<-c\n\n\t\terr := persist(term, history)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"failed to properly clean up terminal: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tos.Exit(0)\n\t}()\n\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn term, err\n\t}\n\tdefer f.Close()\n\t_, err = term.ReadHistory(f)\n\treturn term, err\n}\n\nfunc persist(term *liner.State, path string) error {\n\tf, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not open %q to append history: %v\", path, err)\n\t}\n\tdefer f.Close()\n\t_, err = term.WriteHistory(f)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not write history to %q: %v\", path, err)\n\t}\n\treturn term.Close()\n}\n"
  },
  {
    "path": "internal/repl/repl_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage repl\n\nimport (\n\t\"testing\"\n)\n\nvar testSplitLines = []struct {\n\tline              string\n\texpectedCommand   string\n\texpectedArguments string\n\terr               error\n}{\n\t{\n\t\tline:              \":a arg1 arg2 arg3 .\",\n\t\texpectedCommand:   \":a\",\n\t\texpectedArguments: \" arg1 arg2 arg3 .\",\n\t},\n\t{\n\t\tline:              \":debug t\",\n\t\texpectedCommand:   \":debug\",\n\t\texpectedArguments: \" t\",\n\t},\n\t{\n\t\tline: \"\",\n\t\t// expectedCommand is nil\n\t\t// expectedArguments is nil\n\t},\n\t{\n\t\tline:              `:d <http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`,\n\t\texpectedCommand:   \":d\",\n\t\texpectedArguments: ` <http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`,\n\t},\n\t{\n\t\tline:              `  :a  subject  \"predicate with spaces\" object  . `,\n\t\texpectedCommand:   \":a\",\n\t\texpectedArguments: `  subject  \"predicate with spaces\" object  .`,\n\t},\n}\n\nfunc TestSplitLines(t *testing.T) {\n\tfor _, testcase := range testSplitLines {\n\t\tcommand, arguments := splitLine(testcase.line)\n\n\t\tif testcase.expectedCommand != command {\n\t\t\tt.Errorf(\"Error splitting lines: got: %v expected: %v\", command, testcase.expectedCommand)\n\t\t}\n\n\t\tif testcase.expectedArguments != arguments {\n\t\t\tt.Errorf(\"Error splitting lines: got: %v expected: %v\", arguments, testcase.expectedArguments)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "query/gizmo/environ.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\n// Builds a new Gizmo environment pointing at a session.\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"time\"\n\n\t\"github.com/dop251/goja\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\n// graphObject is a root graph object.\n//\n// Name: `graph`, Alias: `g`\n//\n// This is the only special object in the environment, generates the query objects.\n// Under the hood, they're simple objects that get compiled to a Go iterator tree when executed.\n// Methods starting with \"New\" are accessible in JavaScript with a capital letter (e.g. NewV becomes V)\ntype graphObject struct {\n\ts *Session\n}\n\n// Uri creates an IRI values from a given string.\nfunc (g *graphObject) NewIRI(s string) quad.IRI {\n\treturn quad.IRI(g.s.ns.FullIRI(s))\n}\n\n// AddNamespace associates prefix with a given IRI namespace.\nfunc (g *graphObject) AddNamespace(pref, ns string) {\n\tg.s.ns.Register(voc.Namespace{Prefix: pref + \":\", Full: ns})\n}\n\n// AddDefaultNamespaces register all default namespaces for automatic IRI resolution.\nfunc (g *graphObject) AddDefaultNamespaces() {\n\tvoc.CloneTo(&g.s.ns)\n}\n\n// LoadNamespaces loads all namespaces saved to graph.\nfunc (g *graphObject) LoadNamespaces() error {\n\treturn g.s.sch.LoadNamespaces(g.s.ctx, g.s.qs, &g.s.ns)\n}\n\n// V is a shorthand for Vertex.\nfunc (g *graphObject) NewV(call goja.FunctionCall) goja.Value {\n\treturn g.NewVertex(call)\n}\n\n// Vertex starts a query path at the given vertex/vertices. No ids means \"all vertices\".\n// Signature: ([nodeId],[nodeId]...)\n//\n// Arguments:\n//\n// * `nodeId` (Optional): A string or list of strings representing the starting vertices.\n//\n// Returns: Path object\nfunc (g *graphObject) NewVertex(call goja.FunctionCall) goja.Value {\n\tqv, err := toQuadValues(exportArgs(call.Arguments))\n\tif err != nil {\n\t\treturn throwErr(g.s.vm, err)\n\t}\n\treturn g.s.vm.ToValue(&pathObject{\n\t\ts:      g.s,\n\t\tfinals: true,\n\t\tpath:   path.StartMorphism(qv...),\n\t})\n}\n\n// M is a shorthand for Morphism.\nfunc (g *graphObject) NewM() *pathObject {\n\treturn g.NewMorphism()\n}\n\n// Morphism creates a morphism path object. Unqueryable on it's own, defines one end of the path.\n// Saving these to variables with\n//\n//\t// javascript\n//\tvar shorterPath = graph.Morphism().out(\"foo\").out(\"bar\")\n//\n// is the common use case. See also: path.follow(), path.followR().\nfunc (g *graphObject) NewMorphism() *pathObject {\n\treturn &pathObject{\n\t\ts:    g.s,\n\t\tpath: path.StartMorphism(),\n\t}\n}\n\n// Emit adds data programmatically to the JSON result list. Can be any JSON type.\n//\n//\t// javascript\n//\tg.emit({name:\"bob\"}) // push {\"name\":\"bob\"} as a result\nfunc (g *graphObject) Emit(call goja.FunctionCall) goja.Value {\n\tvalue := call.Argument(0)\n\tif !goja.IsNull(value) && !goja.IsUndefined(value) {\n\t\tval := exportArgs([]goja.Value{value})[0]\n\t\tif val != nil {\n\t\t\tg.s.send(nil, &Result{Val: val})\n\t\t}\n\t}\n\treturn goja.Null()\n}\n\n// Backwards compatibility\nfunc (g *graphObject) CapitalizedUri(s string) quad.IRI {\n\treturn g.NewIRI(s)\n}\nfunc (g *graphObject) CapitalizedAddNamespace(pref, ns string) {\n\tg.AddNamespace(pref, ns)\n}\nfunc (g *graphObject) CapitalizedAddDefaultNamespaces() {\n\tg.AddDefaultNamespaces()\n}\nfunc (g *graphObject) CapitalizedLoadNamespaces() error {\n\treturn g.LoadNamespaces()\n}\nfunc (g *graphObject) CapitalizedEmit(call goja.FunctionCall) goja.Value {\n\treturn g.Emit(call)\n}\n\nfunc oneStringType(fnc func(s string) quad.Value) func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\treturn func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\t\targs := toStrings(exportArgs(call.Arguments))\n\t\tif len(args) != 1 {\n\t\t\treturn throwErr(vm, errArgCount2{Expected: 1, Got: len(args)})\n\t\t}\n\t\treturn vm.ToValue(fnc(args[0]))\n\t}\n}\n\nfunc twoStringType(fnc func(s1, s2 string) quad.Value) func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\treturn func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\t\targs := toStrings(exportArgs(call.Arguments))\n\t\tif len(args) != 2 {\n\t\t\treturn throwErr(vm, errArgCount2{Expected: 2, Got: len(args)})\n\t\t}\n\t\treturn vm.ToValue(fnc(args[0], args[1]))\n\t}\n}\n\nfunc cmpOpType(op iterator.Operator) func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\treturn func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\t\targs := exportArgs(call.Arguments)\n\t\tif len(args) != 1 {\n\t\t\treturn throwErr(vm, errArgCount2{Expected: 1, Got: len(args)})\n\t\t}\n\t\tqv, err := toQuadValue(args[0])\n\t\tif err != nil {\n\t\t\treturn throwErr(vm, err)\n\t\t}\n\t\treturn vm.ToValue(valFilter{f: shape.Comparison{Op: op, Val: qv}})\n\t}\n}\n\nfunc cmpWildcard(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) != 1 {\n\t\treturn throwErr(vm, errArgCount2{Expected: 1, Got: len(args)})\n\t}\n\tpattern, ok := args[0].(string)\n\tif !ok {\n\t\treturn throwErr(vm, fmt.Errorf(\"wildcard: unsupported type: %T\", args[0]))\n\t}\n\treturn vm.ToValue(valFilter{f: shape.Wildcard{Pattern: pattern}})\n}\n\nfunc cmpRegexp(vm *goja.Runtime, call goja.FunctionCall) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) != 1 && len(args) != 2 {\n\t\treturn throwErr(vm, errArgCount2{Expected: 1, Got: len(args)})\n\t}\n\tv, err := toQuadValue(args[0])\n\tif err != nil {\n\t\treturn throwErr(vm, err)\n\t}\n\tallowRefs := false\n\tif len(args) > 1 {\n\t\tb, ok := args[1].(bool)\n\t\tif !ok {\n\t\t\treturn throwErr(vm, fmt.Errorf(\"expected bool as second argument\"))\n\t\t}\n\t\tallowRefs = b\n\t}\n\tswitch vt := v.(type) {\n\tcase quad.String:\n\t\tif allowRefs {\n\t\t\tv = quad.IRI(string(vt))\n\t\t}\n\tcase quad.IRI:\n\t\tif !allowRefs {\n\t\t\treturn throwErr(vm, errRegexpOnIRI)\n\t\t}\n\tcase quad.BNode:\n\t\tif !allowRefs {\n\t\t\treturn throwErr(vm, errRegexpOnIRI)\n\t\t}\n\tdefault:\n\t\treturn throwErr(vm, fmt.Errorf(\"regexp: unsupported type: %T\", v))\n\t}\n\tvar (\n\t\ts    string\n\t\trefs bool\n\t)\n\tswitch v := v.(type) {\n\tcase quad.String:\n\t\ts = string(v)\n\tcase quad.IRI:\n\t\ts, refs = string(v), true\n\tcase quad.BNode:\n\t\ts, refs = string(v), true\n\tdefault:\n\t\treturn throwErr(vm, fmt.Errorf(\"regexp from non-string value: %T\", v))\n\t}\n\tre, err := regexp.Compile(string(s))\n\tif err != nil {\n\t\treturn throwErr(vm, err)\n\t}\n\treturn vm.ToValue(valFilter{f: shape.Regexp{Re: re, Refs: refs}})\n}\n\ntype valFilter struct {\n\tf shape.ValueFilter\n}\n\nvar defaultEnv = map[string]func(vm *goja.Runtime, call goja.FunctionCall) goja.Value{\n\t\"iri\":   oneStringType(func(s string) quad.Value { return quad.IRI(s) }),\n\t\"bnode\": oneStringType(func(s string) quad.Value { return quad.BNode(s) }),\n\t\"raw\":   oneStringType(func(s string) quad.Value { return quad.Raw(s) }),\n\t\"str\":   oneStringType(func(s string) quad.Value { return quad.String(s) }),\n\n\t\"lang\": twoStringType(func(s, lang string) quad.Value {\n\t\treturn quad.LangString{Value: quad.String(s), Lang: lang}\n\t}),\n\t\"typed\": twoStringType(func(s, typ string) quad.Value {\n\t\treturn quad.TypedString{Value: quad.String(s), Type: quad.IRI(typ)}\n\t}),\n\n\t\"lt\":    cmpOpType(iterator.CompareLT),\n\t\"lte\":   cmpOpType(iterator.CompareLTE),\n\t\"gt\":    cmpOpType(iterator.CompareGT),\n\t\"gte\":   cmpOpType(iterator.CompareGTE),\n\t\"regex\": cmpRegexp,\n\t\"like\":  cmpWildcard,\n}\n\nfunc unwrap(o interface{}) interface{} {\n\tswitch v := o.(type) {\n\tcase *pathObject:\n\t\to = v.path\n\tcase []interface{}:\n\t\tfor i, val := range v {\n\t\t\tv[i] = unwrap(val)\n\t\t}\n\tcase map[string]interface{}:\n\t\tfor k, val := range v {\n\t\t\tv[k] = unwrap(val)\n\t\t}\n\t}\n\treturn o\n}\n\nfunc exportArgs(args []goja.Value) []interface{} {\n\tif len(args) == 0 {\n\t\treturn nil\n\t}\n\tout := make([]interface{}, 0, len(args))\n\tfor _, a := range args {\n\t\to := a.Export()\n\t\tout = append(out, unwrap(o))\n\t}\n\treturn out\n}\n\nfunc toInt(o interface{}) (int, bool) {\n\tswitch v := o.(type) {\n\tcase int:\n\t\treturn v, true\n\tcase int64:\n\t\treturn int(v), true\n\tcase float64:\n\t\treturn int(v), true\n\tdefault:\n\t\treturn 0, false\n\t}\n}\n\nfunc toQuadValue(o interface{}) (quad.Value, error) {\n\tvar qv quad.Value\n\tswitch v := o.(type) {\n\tcase quad.Value:\n\t\tqv = v\n\tcase string:\n\t\tqv = quad.StringToValue(v)\n\tcase bool:\n\t\tqv = quad.Bool(v)\n\tcase int:\n\t\tqv = quad.Int(v)\n\tcase int64:\n\t\tqv = quad.Int(v)\n\tcase float64:\n\t\tif float64(int(v)) == v {\n\t\t\tqv = quad.Int(int64(v))\n\t\t} else {\n\t\t\tqv = quad.Float(v)\n\t\t}\n\tcase time.Time:\n\t\tqv = quad.Time(v)\n\tdefault:\n\t\treturn nil, errNotQuadValue{Val: o}\n\t}\n\treturn qv, nil\n}\n\nfunc toQuadValues(objs []interface{}) ([]quad.Value, error) {\n\tif len(objs) == 0 {\n\t\treturn nil, nil\n\t}\n\tvals := make([]quad.Value, 0, len(objs))\n\tfor _, o := range objs {\n\t\tqv, err := toQuadValue(o)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvals = append(vals, qv)\n\t}\n\treturn vals, nil\n}\n\nfunc toStrings(objs []interface{}) []string {\n\tif len(objs) == 0 {\n\t\treturn nil\n\t}\n\tvar out = make([]string, 0, len(objs))\n\tfor _, o := range objs {\n\t\tswitch v := o.(type) {\n\t\tcase string:\n\t\t\tout = append(out, v)\n\t\tcase quad.Value:\n\t\t\tout = append(out, quad.StringOf(v))\n\t\tcase []string:\n\t\t\tout = append(out, v...)\n\t\tcase []interface{}:\n\t\t\tout = append(out, toStrings(v)...)\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(\"expected string, got: %T\", o))\n\t\t}\n\t}\n\treturn out\n}\n\nfunc toVia(via []interface{}) []interface{} {\n\tif len(via) == 0 {\n\t\treturn nil\n\t} else if len(via) == 1 {\n\t\tif via[0] == nil {\n\t\t\treturn nil\n\t\t} else if v, ok := via[0].([]interface{}); ok {\n\t\t\treturn toVia(v)\n\t\t} else if v, ok := via[0].([]string); ok {\n\t\t\tarr := make([]interface{}, 0, len(v))\n\t\t\tfor _, s := range v {\n\t\t\t\tarr = append(arr, s)\n\t\t\t}\n\t\t\treturn toVia(arr)\n\t\t}\n\t}\n\tfor i := range via {\n\t\tif _, ok := via[i].(*path.Path); ok {\n\t\t\t// bypass\n\t\t} else if vp, ok := via[i].(*pathObject); ok {\n\t\t\tvia[i] = vp.path\n\t\t} else if qv, err := toQuadValue(via[i]); err == nil {\n\t\t\tvia[i] = qv\n\t\t} else {\n\t\t\tpanic(fmt.Errorf(\"unsupported type: %T\", via[i]))\n\t\t}\n\t}\n\treturn via\n}\n\nfunc toViaData(objs []interface{}) (predicates []interface{}, tags []string, ok bool) {\n\tif len(objs) != 0 {\n\t\tpredicates = toVia([]interface{}{objs[0]})\n\t}\n\tif len(objs) > 1 {\n\t\ttags = toStrings(objs[1:])\n\t}\n\tok = true\n\treturn\n}\n\nfunc toViaDepthData(objs []interface{}) (predicates []interface{}, maxDepth int, tags []string, ok bool) {\n\tif len(objs) != 0 {\n\t\tpredicates = toVia([]interface{}{objs[0]})\n\t}\n\tif len(objs) > 1 {\n\t\tmaxDepth, ok = toInt(objs[1])\n\t\tif ok {\n\t\t\tif len(objs) > 2 {\n\t\t\t\ttags = toStrings(objs[2:])\n\t\t\t}\n\t\t} else {\n\t\t\ttags = toStrings(objs[1:])\n\t\t}\n\t}\n\tok = true\n\treturn\n}\n\nfunc throwErr(vm *goja.Runtime, err error) goja.Value {\n\tpanic(vm.ToValue(err))\n}\n"
  },
  {
    "path": "query/gizmo/errors.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\nimport \"fmt\"\n\nvar (\n\terrNoVia       = fmt.Errorf(\"expected predicate list\")\n\terrRegexpOnIRI = fmt.Errorf(\"regexps are not allowed on IRIs\")\n)\n\ntype errArgCount2 struct {\n\tExpected int\n\tGot      int\n}\n\nfunc (e errArgCount2) Error() string {\n\treturn fmt.Sprintf(\"expected %d argument, got %d\", e.Expected, e.Got)\n}\n\ntype errArgCount struct {\n\tGot int\n}\n\nfunc (e errArgCount) Error() string {\n\treturn fmt.Sprintf(\"unexpected arguments count: %d\", e.Got)\n}\n\ntype errNotQuadValue struct {\n\tVal interface{}\n}\n\nfunc (e errNotQuadValue) Error() string {\n\treturn fmt.Sprintf(\"not a quad.Value: %T\", e.Val)\n}\n"
  },
  {
    "path": "query/gizmo/finals.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\nimport (\n\t\"github.com/dop251/goja\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nconst TopResultTag = \"id\"\n\n// GetLimit is the same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals.\nfunc (p *pathObject) GetLimit(limit int) error {\n\tit := p.buildIteratorTree()\n\tit = iterator.Tag(it, TopResultTag)\n\tp.s.limit = limit\n\tp.s.count = 0\n\treturn p.s.runIterator(it)\n}\n\n// All executes the query and adds the results, with all tags, as a string-to-string (tag to node) map in the output set, one for each path that a traversal could take.\nfunc (p *pathObject) All() error {\n\treturn p.GetLimit(p.s.limit)\n}\n\nfunc (p *pathObject) toArray(call goja.FunctionCall, withTags bool) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) > 1 {\n\t\treturn throwErr(p.s.vm, errArgCount2{Expected: 1, Got: len(args)})\n\t}\n\tlimit := -1\n\tif len(args) > 0 {\n\t\tlimit, _ = toInt(args[0])\n\t}\n\tit := p.buildIteratorTree()\n\tit = iterator.Tag(it, TopResultTag)\n\tvar (\n\t\tarray interface{}\n\t\terr   error\n\t)\n\tif !withTags {\n\t\tarray, err = p.s.runIteratorToArrayNoTags(it, limit)\n\t} else {\n\t\tarray, err = p.s.runIteratorToArray(it, limit)\n\t}\n\tif err != nil {\n\t\treturn throwErr(p.s.vm, err)\n\t}\n\treturn p.s.vm.ToValue(array)\n}\n\n// ToArray executes a query and returns the results at the end of the query path as an JS array.\n//\n// Example:\n// \t// javascript\n//\t// bobFollowers contains an Array of followers of bob (alice, charlie, dani).\n//\tvar bobFollowers = g.V(\"<bob>\").In(\"<follows>\").ToArray()\nfunc (p *pathObject) ToArray(call goja.FunctionCall) goja.Value {\n\treturn p.toArray(call, false)\n}\n\n// TagArray is the same as ToArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment.\n//\n// Example:\n// \t// javascript\n//\t// bobTags contains an Array of followers of bob (alice, charlie, dani).\n//\tvar bobTags = g.V(\"<bob>\").Tag(\"name\").In(\"<follows>\").TagArray()\n//\t// nameValue should be the string \"<bob>\"\n//\tvar nameValue = bobTags[0][\"name\"]\nfunc (p *pathObject) TagArray(call goja.FunctionCall) goja.Value {\n\treturn p.toArray(call, true)\n}\nfunc (p *pathObject) toValue(withTags bool) (interface{}, error) {\n\tit := p.buildIteratorTree()\n\tit = iterator.Tag(it, TopResultTag)\n\tconst limit = 1\n\tif !withTags {\n\t\tarray, err := p.s.runIteratorToArrayNoTags(it, limit)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(array) == 0 {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn array[0], nil\n\t}\n\tarray, err := p.s.runIteratorToArray(it, limit)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(array) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn array[0], nil\n}\n\n// ToValue is the same as ToArray, but limited to one result node.\nfunc (p *pathObject) ToValue() (interface{}, error) {\n\treturn p.toValue(false)\n}\n\n// TagValue is the same as TagArray, but limited to one result node. Returns a tag-to-string map.\nfunc (p *pathObject) TagValue() (interface{}, error) {\n\treturn p.toValue(true)\n}\n\n// Map is a alias for ForEach.\nfunc (p *pathObject) Map(call goja.FunctionCall) goja.Value {\n\treturn p.ForEach(call)\n}\n\n// ForEach calls callback(data) for each result, where data is the tag-to-string map as in All case.\n// Signature: (callback) or (limit, callback)\n//\n// Arguments:\n//\n// * `limit` (Optional): An integer value on the first `limit` paths to process.\n// * `callback`: A javascript function of the form `function(data)`\n//\n// Example:\n// \t// javascript\n//\t// Simulate query.All().All()\n//\tgraph.V(\"<alice>\").ForEach(function(d) { g.Emit(d) } )\nfunc (p *pathObject) ForEach(call goja.FunctionCall) goja.Value {\n\tit := p.buildIteratorTree()\n\tit = iterator.Tag(it, TopResultTag)\n\tif n := len(call.Arguments); n != 1 && n != 2 {\n\t\treturn throwErr(p.s.vm, errArgCount{Got: len(call.Arguments)})\n\t}\n\tcallback := call.Argument(len(call.Arguments) - 1)\n\targs := exportArgs(call.Arguments[:len(call.Arguments)-1])\n\tlimit := -1\n\tif len(args) != 0 {\n\t\tlimit, _ = toInt(args[0])\n\t}\n\terr := p.s.runIteratorWithCallback(it, callback, call, limit)\n\tif err != nil {\n\t\treturn throwErr(p.s.vm, err)\n\t}\n\treturn goja.Null()\n}\n\n// Count returns a number of results and returns it as a value.\n//\n// Example:\n//\t// javascript\n//\t// Save count as a variable\n//\tvar n = g.V().count()\n//\t// Send it as a query result\n//\tg.emit(n)\nfunc (p *pathObject) Count() (int64, error) {\n\tit := p.buildIteratorTree()\n\treturn p.s.countResults(it)\n}\n\n// Backwards compatibility\nfunc (p *pathObject) CapitalizedGetLimit(limit int) error {\n\treturn p.GetLimit(limit)\n}\nfunc (p *pathObject) CapitalizedAll() error {\n\treturn p.All()\n}\nfunc (p *pathObject) CapitalizedtoArray(call goja.FunctionCall, withTags bool) goja.Value {\n\treturn p.toArray(call, withTags)\n}\nfunc (p *pathObject) CapitalizedToArray(call goja.FunctionCall) goja.Value {\n\treturn p.ToArray(call)\n}\nfunc (p *pathObject) CapitalizedTagArray(call goja.FunctionCall) goja.Value {\n\treturn p.TagArray(call)\n}\nfunc (p *pathObject) CapitalizedtoValue(withTags bool) (interface{}, error) {\n\treturn p.toValue(withTags)\n}\nfunc (p *pathObject) CapitalizedToValue() (interface{}, error) {\n\treturn p.ToValue()\n}\nfunc (p *pathObject) CapitalizedTagValue() (interface{}, error) {\n\treturn p.TagValue()\n}\nfunc (p *pathObject) CapitalizedMap(call goja.FunctionCall) goja.Value {\n\treturn p.Map(call)\n}\nfunc (p *pathObject) CapitalizedForEach(call goja.FunctionCall) goja.Value {\n\treturn p.ForEach(call)\n}\nfunc (p *pathObject) CapitalizedCount() (int64, error) {\n\treturn p.Count()\n}\n\nfunc quadValueToString(v quad.Value) string {\n\tif s, ok := v.(quad.String); ok {\n\t\treturn string(s)\n\t}\n\treturn quad.StringOf(v)\n}\n"
  },
  {
    "path": "query/gizmo/gizmo.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/dop251/goja\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nconst Name = \"gizmo\"\n\nfunc init() {\n\tquery.RegisterLanguage(query.Language{\n\t\tName: Name,\n\t\tSession: func(qs graph.QuadStore) query.Session {\n\t\t\treturn NewSession(qs)\n\t\t},\n\t})\n}\n\nfunc NewSession(qs graph.QuadStore) *Session {\n\ts := &Session{\n\t\tctx: context.Background(),\n\t\tsch: schema.NewConfig(),\n\t\tqs:  qs, limit: -1,\n\t}\n\tif err := s.buildEnv(); err != nil {\n\t\tpanic(err)\n\t}\n\treturn s\n}\n\nfunc lcFirst(str string) string {\n\trune, size := utf8.DecodeRuneInString(str)\n\treturn string(unicode.ToLower(rune)) + str[size:]\n}\n\ntype fieldNameMapper struct{}\n\nfunc (fieldNameMapper) FieldName(t reflect.Type, f reflect.StructField) string {\n\treturn lcFirst(f.Name)\n}\n\nconst constructMethodPrefix = \"New\"\nconst backwardsCompatibilityPrefix = \"Capitalized\"\n\nfunc (fieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string {\n\tif strings.HasPrefix(m.Name, backwardsCompatibilityPrefix) {\n\t\treturn strings.TrimPrefix(m.Name, backwardsCompatibilityPrefix)\n\t}\n\tif strings.HasPrefix(m.Name, constructMethodPrefix) {\n\t\treturn strings.TrimPrefix(m.Name, constructMethodPrefix)\n\t}\n\treturn lcFirst(m.Name)\n}\n\ntype Session struct {\n\tqs  graph.QuadStore\n\tvm  *goja.Runtime\n\tns  voc.Namespaces\n\tsch *schema.Config\n\tcol query.Collation\n\n\tlast string\n\tp    *goja.Program\n\n\tout   chan *Result\n\tctx   context.Context\n\tlimit int\n\tcount int\n\n\terr error\n}\n\nfunc (s *Session) context() context.Context {\n\treturn s.ctx\n}\n\nfunc (s *Session) buildEnv() error {\n\tif s.vm != nil {\n\t\treturn nil\n\t}\n\ts.vm = goja.New()\n\ts.vm.SetFieldNameMapper(fieldNameMapper{})\n\ts.vm.Set(\"graph\", &graphObject{s: s})\n\ts.vm.Set(\"g\", s.vm.Get(\"graph\"))\n\tfor name, val := range defaultEnv {\n\t\tfnc := val\n\t\ts.vm.Set(name, func(call goja.FunctionCall) goja.Value {\n\t\t\treturn fnc(s.vm, call)\n\t\t})\n\t}\n\treturn nil\n}\n\nfunc (s *Session) quadValueToNative(v quad.Value) interface{} {\n\tif v == nil {\n\t\treturn nil\n\t}\n\tif s.col == query.JSONLD {\n\t\treturn jsonld.FromValue(v)\n\t}\n\tout := v.Native()\n\tif nv, ok := out.(quad.Value); ok && v == nv {\n\t\treturn quad.StringOf(v)\n\t}\n\treturn out\n}\n\nfunc (s *Session) tagsToValueMap(m map[string]graph.Ref) (map[string]interface{}, error) {\n\toutputMap := make(map[string]interface{})\n\tfor k, v := range m {\n\t\tnv, err := s.qs.NameOf(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif o := s.quadValueToNative(nv); o != nil {\n\t\t\toutputMap[k] = o\n\t\t}\n\t}\n\tif len(outputMap) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn outputMap, nil\n}\nfunc (s *Session) runIteratorToArray(it iterator.Shape, limit int) ([]map[string]interface{}, error) {\n\tctx := s.context()\n\n\toutput := make([]map[string]interface{}, 0)\n\terr := iterator.Iterate(ctx, it).Limit(limit).TagEach(func(tags map[string]graph.Ref) error {\n\t\ttm, err := s.tagsToValueMap(tags)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif tm != nil {\n\t\t\toutput = append(output, tm)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn output, nil\n}\n\nfunc (s *Session) runIteratorToArrayNoTags(it iterator.Shape, limit int) ([]interface{}, error) {\n\tctx := s.context()\n\n\toutput := make([]interface{}, 0)\n\terr := iterator.Iterate(ctx, it).Paths(false).Limit(limit).EachValue(s.qs, func(v quad.Value) error {\n\t\tif o := s.quadValueToNative(v); o != nil {\n\t\t\toutput = append(output, o)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn output, nil\n}\n\nfunc (s *Session) runIteratorWithCallback(it iterator.Shape, callback goja.Value, this goja.FunctionCall, limit int) error {\n\tfnc, ok := goja.AssertFunction(callback)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected js callback function\")\n\t}\n\tctx, cancel := context.WithCancel(s.context())\n\tdefer cancel()\n\treturn iterator.Iterate(ctx, it).Paths(true).Limit(limit).TagEach(func(tags map[string]graph.Ref) error {\n\t\ttm, err := s.tagsToValueMap(tags)\n\t\tif err != nil || tm == nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = fnc(this.This, s.vm.ToValue(tm))\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t}\n\t\treturn err\n\t})\n}\n\nfunc (s *Session) send(ctx context.Context, r *Result) bool {\n\tif s.limit > 0 && s.count >= s.limit {\n\t\treturn false\n\t}\n\tif s.out == nil {\n\t\treturn false\n\t}\n\tif ctx == nil {\n\t\tctx = s.ctx\n\t}\n\tselect {\n\tcase s.out <- r:\n\tcase <-ctx.Done():\n\t\treturn false\n\t}\n\ts.count++\n\treturn s.limit <= 0 || s.count < s.limit\n}\n\nfunc (s *Session) runIterator(it iterator.Shape) error {\n\tctx, cancel := context.WithCancel(s.context())\n\tdefer cancel()\n\tstop := false\n\terr := iterator.Iterate(ctx, it).Paths(true).TagEach(func(tags map[string]graph.Ref) error {\n\t\tif !s.send(ctx, &Result{Tags: tags}) {\n\t\t\tcancel()\n\t\t\tstop = true\n\t\t}\n\t\treturn nil\n\t})\n\tif stop {\n\t\terr = nil\n\t}\n\treturn err\n}\n\nfunc (s *Session) countResults(it iterator.Shape) (int64, error) {\n\treturn iterator.Iterate(s.context(), it).Paths(true).Count()\n}\n\ntype Result struct {\n\tMeta bool\n\tVal  interface{}\n\tTags map[string]graph.Ref\n}\n\nfunc (r *Result) Result() interface{} {\n\tif r.Tags != nil {\n\t\treturn r.Tags\n\t}\n\treturn r.Val\n}\n\nfunc (s *Session) compile(qu string) error {\n\tvar p *goja.Program\n\tif s.last == qu && s.last != \"\" {\n\t\tp = s.p\n\t} else {\n\t\tvar err error\n\t\tp, err = goja.Compile(\"\", qu, false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.last, s.p = qu, p\n\t}\n\treturn nil\n}\n\nfunc (s *Session) run() (goja.Value, error) {\n\tv, err := s.vm.RunProgram(s.p)\n\tif e, ok := err.(*goja.Exception); ok && e.Value() != nil {\n\t\tif er, ok := e.Value().Export().(error); ok {\n\t\t\terr = er\n\t\t}\n\t}\n\treturn v, err\n}\nfunc (s *Session) Execute(ctx context.Context, qu string, opt query.Options) (query.Iterator, error) {\n\tswitch opt.Collation {\n\tcase query.Raw, query.JSON, query.JSONLD, query.REPL:\n\tdefault:\n\t\treturn nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}\n\t}\n\tif err := s.compile(qu); err != nil {\n\t\treturn nil, err\n\t}\n\ts.limit = opt.Limit\n\ts.count = 0\n\tctx, cancel := context.WithCancel(context.Background())\n\ts.ctx = ctx\n\ts.col = opt.Collation\n\treturn &results{\n\t\tcol: opt.Collation,\n\t\ts:   s,\n\t\tctx: ctx, cancel: cancel,\n\t}, nil\n}\n\ntype results struct {\n\ts      *Session\n\tcol    query.Collation\n\tctx    context.Context\n\tcancel func()\n\n\trunning bool\n\terrc    chan error\n\n\terr error\n\tcur *Result\n}\n\nfunc (it *results) stop(err error) {\n\tit.cancel()\n\tif !it.running {\n\t\treturn\n\t}\n\tit.s.vm.Interrupt(err)\n\tit.running = false\n}\n\nfunc (it *results) Next(ctx context.Context) bool {\n\tif it.errc == nil {\n\t\tit.s.out = make(chan *Result)\n\t\tit.errc = make(chan error, 1)\n\t\tit.running = true\n\t\tgo func() {\n\t\t\tdefer close(it.errc)\n\t\t\tv, err := it.s.run()\n\t\t\tif err != nil {\n\t\t\t\tit.errc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !goja.IsNull(v) && !goja.IsUndefined(v) {\n\t\t\t\tit.s.send(it.ctx, &Result{Meta: true, Val: v.Export()})\n\t\t\t}\n\t\t}()\n\t}\n\tselect {\n\tcase r := <-it.s.out:\n\t\tit.cur = r\n\t\treturn true\n\tcase err := <-it.errc:\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t}\n\t\treturn false\n\tcase <-ctx.Done():\n\t\tit.err = ctx.Err()\n\t\tit.stop(it.err)\n\t\treturn false\n\t}\n}\n\nfunc (it *results) Result() interface{} {\n\tif it.cur == nil {\n\t\treturn nil\n\t}\n\tswitch it.col {\n\tcase query.Raw:\n\t\treturn it.cur\n\tcase query.JSON, query.JSONLD:\n\t\treturn it.jsonResult()\n\tcase query.REPL:\n\t\treturn it.replResult()\n\t}\n\treturn nil\n}\n\nfunc (it *results) jsonResult() interface{} {\n\tdata := it.cur\n\tif data.Meta {\n\t\treturn nil\n\t}\n\tif data.Val != nil {\n\t\treturn data.Val\n\t}\n\tobj := make(map[string]interface{})\n\ttags := data.Tags\n\tvar tagKeys []string\n\tfor k := range tags {\n\t\ttagKeys = append(tagKeys, k)\n\t}\n\tsort.Strings(tagKeys)\n\tfor _, k := range tagKeys {\n\t\tname, err := it.s.qs.NameOf(tags[k])\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t\treturn nil\n\t\t}\n\t\tif name != nil {\n\t\t\tobj[k] = it.s.quadValueToNative(name)\n\t\t} else {\n\t\t\tdelete(obj, k)\n\t\t}\n\t}\n\treturn obj\n}\n\nfunc (it *results) replResult() interface{} {\n\tdata := it.cur\n\tif data.Meta {\n\t\tif data.Val != nil {\n\t\t\ts := data.Val\n\t\t\tswitch s.(type) {\n\t\t\tcase *pathObject, *graphObject:\n\t\t\t\ts = \"[internal Iterator]\"\n\t\t\t}\n\t\t\treturn fmt.Sprintln(\"=>\", s)\n\t\t}\n\t\treturn fmt.Sprintln(\"=>\", nil)\n\t}\n\tvar out string\n\tout = fmt.Sprintln(\"****\")\n\tif data.Val == nil {\n\t\ttags := data.Tags\n\t\ttagKeys := make([]string, len(tags))\n\t\ti := 0\n\t\tfor k := range tags {\n\t\t\ttagKeys[i] = k\n\t\t\ti++\n\t\t}\n\t\tsort.Strings(tagKeys)\n\t\tfor _, k := range tagKeys {\n\t\t\tif k == \"$_\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tknv, err := it.s.qs.NameOf(tags[k])\n\t\t\tif err != nil {\n\t\t\t\t// ignore\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tout += fmt.Sprintf(\"%s : %s\\n\", k, quadValueToString(knv))\n\t\t}\n\t} else {\n\t\tswitch export := data.Val.(type) {\n\t\tcase map[string]string:\n\t\t\tfor k, v := range export {\n\t\t\t\tout += fmt.Sprintf(\"%s : %s\\n\", k, v)\n\t\t\t}\n\t\tcase map[string]interface{}:\n\t\t\tfor k, v := range export {\n\t\t\t\tout += fmt.Sprintf(\"%s : %v\\n\", k, v)\n\t\t\t}\n\t\tdefault:\n\t\t\tout += fmt.Sprintf(\"%s\\n\", data.Val)\n\t\t}\n\t}\n\treturn out\n}\n\nfunc (it *results) Err() error {\n\treturn it.err\n}\n\nfunc (it *results) Close() error {\n\tit.stop(errors.New(\"iterator closed\"))\n\treturn nil\n}\n"
  },
  {
    "path": "query/gizmo/gizmo_test.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/cayleygraph/quad\"\n\n\t// register global namespace for tests\n\t_ \"github.com/cayleygraph/quad/voc/rdf\"\n)\n\n// This is a simple test graph used for testing\n//\n//  +-------+                        +------+\n//  | alice |-----                 ->| fred |<--\n//  +-------+     \\---->+-------+-/  +------+   \\-+-------+\n//                ----->| #bob# |       |         | emily |\n//  +---------+--/  --->+-------+       |         +-------+\n//  | charlie |    /                    v\n//  +---------+   /                  +--------+\n//    \\---    +--------+             | #greg# |\n//        \\-->| #dani# |------------>+--------+\n//            +--------+\n//\n\nfunc makeTestSession(data []quad.Quad) *Session {\n\tqs, _ := graph.NewQuadStore(\"memstore\", \"\", nil)\n\tw, _ := graph.NewQuadWriter(\"single\", qs, nil)\n\tfor _, t := range data {\n\t\tw.AddQuad(t)\n\t}\n\treturn NewSession(qs)\n}\n\nfunc intVal(v int) string {\n\treturn quad.Int(v).String()\n}\n\nconst multiGraphTestFile = \"../../data/testdata_multigraph.nq\"\n\nvar testQueries = []struct {\n\tmessage string\n\tdata    []quad.Quad\n\tquery   string\n\tlimit   int\n\ttag     string\n\tfile    string\n\texpect  []string\n\terr     bool // TODO(dennwc): define error types for Gizmo and handle them\n}{\n\t// Simple query tests.\n\t{\n\t\tmessage: \"get a single vertex\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\").all()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"get a single vertex (legacy)\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\").All()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"get a single vertex (legacy)\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\").All()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"use .getLimit\",\n\t\tquery: `\n\t\t\tg.V().getLimit(5)\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<bob>\", \"<follows>\", \"<fred>\", \"<status>\"},\n\t},\n\t{\n\t\tmessage: \"get a single vertex (IRI)\",\n\t\tquery: `\n\t\t\tg.V(iri(\"alice\")).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"use .out()\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\").out(\"<follows>\").all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"use .out() (IRI)\",\n\t\tquery: `\n\t\t\tg.V(iri(\"alice\")).out(iri(\"follows\")).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"use .out() (any)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").out().all()\n\t\t`,\n\t\texpect: []string{\"<fred>\", \"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"use .in()\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").all()\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() (any)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in().all()\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter()\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(gt(iri(\"c\")),lt(iri(\"d\"))).all()\n\t\t`,\n\t\texpect: []string{\"<charlie>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(regex)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(regex(\"ar?li.*e\")).all()\n\t\t`,\n\t\texpect: nil,\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(prefix)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(like(\"al%\")).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(wildcard)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(like(\"a?i%e\")).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(regex with IRIs)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(regex(\"ar?li.*e\", true)).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\"},\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(regex with IRIs)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(regex(iri(\"ar?li.*e\"))).all()\n\t\t`,\n\t\terr: true,\n\t},\n\t{\n\t\tmessage: \"use .in() with .filter(regex,gt)\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").filter(regex(\"ar?li.*e\", true),gt(iri(\"c\"))).all()\n\t\t`,\n\t\texpect: []string{\"<charlie>\"},\n\t},\n\t{\n\t\tmessage: \"filter with a wrong type\",\n\t\tquery: `\n\t\t\tg.V().filter(/<alice>/).all()\n\t\t`,\n\t\terr: true,\n\t},\n\t{\n\t\tmessage: \"use .both()\",\n\t\tquery: `\n\t\t\tg.V(\"<fred>\").both(\"<follows>\").all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<greg>\", \"<emily>\"},\n\t},\n\t{\n\t\tmessage: \"use .both() with tag\",\n\t\tquery: `\n\t\t\tg.V(\"<fred>\").both(null, \"pred\").all()\n\t\t`,\n\t\ttag:    \"pred\",\n\t\texpect: []string{\"<follows>\", \"<follows>\", \"<follows>\"},\n\t},\n\t{\n\t\tmessage: \"use .tag()-.is()-.back()\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").tag(\"foo\").out(\"<status>\").is(\"cool_person\").back(\"foo\").all()\n\t\t`,\n\t\texpect: []string{\"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"separate .tag()-.is()-.back()\",\n\t\tquery: `\n\t\t\tx = g.V(\"<charlie>\").out(\"<follows>\").tag(\"foo\").out(\"<status>\").is(\"cool_person\").back(\"foo\")\n\t\t\tx.in(\"<follows>\").is(\"<dani>\").back(\"foo\").all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"do multiple .back()\",\n\t\tquery: `\n\t\t\tg.V(\"<emily>\").out(\"<follows>\").as(\"f\").out(\"<follows>\").out(\"<status>\").is(\"cool_person\").back(\"f\").in(\"<follows>\").in(\"<follows>\").as(\"acd\").out(\"<status>\").is(\"cool_person\").back(\"f\").all()\n\t\t`,\n\t\ttag:    \"acd\",\n\t\texpect: []string{\"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"use Except to filter out a single vertex\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\", \"<bob>\").except(g.V(\"<alice>\")).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"use chained Except\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\", \"<bob>\", \"<charlie>\").except(g.V(\"<bob>\")).except(g.V(\"<charlie>\")).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\"},\n\t},\n\n\t{\n\t\tmessage: \"use Unique\",\n\t\tquery: `\n\t\t\tg.V(\"<alice>\", \"<bob>\", \"<charlie>\").out(\"<follows>\").unique().all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<dani>\", \"<fred>\"},\n\t},\n\n\t// Morphism tests.\n\t{\n\t\tmessage: \"show simple morphism\",\n\t\tquery: `\n\t\t\tgrandfollows = g.M().out(\"<follows>\").out(\"<follows>\")\n\t\t\tg.V(\"<charlie>\").follow(grandfollows).all()\n\t\t`,\n\t\texpect: []string{\"<greg>\", \"<fred>\", \"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"show reverse morphism\",\n\t\tquery: `\n\t\t\tgrandfollows = g.M().out(\"<follows>\").out(\"<follows>\")\n\t\t\tg.V(\"<fred>\").followR(grandfollows).all()\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\", \"<dani>\"},\n\t},\n\n\t// Intersection tests.\n\t{\n\t\tmessage: \"show simple intersection\",\n\t\tquery: `\n\t\t\tfunction follows(x) { return g.V(x).out(\"<follows>\") }\n\t\t\tfollows(\"<dani>\").and(follows(\"<charlie>\")).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"show simple morphism intersection\",\n\t\tquery: `\n\t\t\tgrandfollows = g.M().out(\"<follows>\").out(\"<follows>\")\n\t\t\tfunction gfollows(x) { return g.V(x).follow(grandfollows) }\n\t\t\tgfollows(\"<alice>\").and(gfollows(\"<charlie>\")).all()\n\t\t`,\n\t\texpect: []string{\"<fred>\"},\n\t},\n\t{\n\t\tmessage: \"show double morphism intersection\",\n\t\tquery: `\n\t\t\tgrandfollows = g.M().out(\"<follows>\").out(\"<follows>\")\n\t\t\tfunction gfollows(x) { return g.V(x).follow(grandfollows) }\n\t\t\tgfollows(\"<emily>\").and(gfollows(\"<charlie>\")).and(gfollows(\"<bob>\")).all()\n\t\t`,\n\t\texpect: []string{\"<greg>\"},\n\t},\n\t{\n\t\tmessage: \"show reverse intersection\",\n\t\tquery: `\n\t\t\tgrandfollows = g.M().out(\"<follows>\").out(\"<follows>\")\n\t\t\tg.V(\"<greg>\").followR(grandfollows).intersect(g.V(\"<fred>\").followR(grandfollows)).all()\n\t\t`,\n\t\texpect: []string{\"<charlie>\"},\n\t},\n\t{\n\t\tmessage: \"show standard sort of morphism intersection, continue follow\",\n\t\tquery: `gfollowers = g.M().in(\"<follows>\").in(\"<follows>\")\n\t\t\tfunction cool(x) { return g.V(x).as(\"a\").out(\"<status>\").is(\"cool_person\").back(\"a\") }\n\t\t\tcool(\"<greg>\").follow(gfollowers).intersect(cool(\"<bob>\").follow(gfollowers)).all()\n\t\t`,\n\t\texpect: []string{\"<charlie>\"},\n\t},\n\t{\n\t\tmessage: \"test Or()\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").out(\"<follows>\").or(g.V().has(\"<status>\", \"cool_person\")).all()\n\t\t`,\n\t\texpect: []string{\"<fred>\", \"<bob>\", \"<greg>\", \"<dani>\"},\n\t},\n\n\t// Has tests.\n\t{\n\t\tmessage: \"show a simple Has\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\", \"cool_person\").all()\n\t\t`,\n\t\texpect: []string{\"<greg>\", \"<dani>\", \"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"show a simple HasR\",\n\t\tquery: `\n\t\t\t\tg.V().hasR(\"<status>\", \"<bob>\").all()\n\t\t`,\n\t\texpect: []string{\"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"show a double Has\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\", \"cool_person\").has(\"<follows>\", \"<fred>\").all()\n\t\t`,\n\t\texpect: []string{\"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"show a Has with filter\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<follows>\", gt(\"<f>\")).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<dani>\", \"<emily>\", \"<fred>\"},\n\t},\n\n\t// Skip/Limit tests.\n\t{\n\t\tmessage: \"use Limit\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\", \"cool_person\").limit(2).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"use Skip\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\", \"cool_person\").skip(2).all()\n\t\t`,\n\t\texpect: []string{\"<greg>\"},\n\t},\n\t{\n\t\tmessage: \"use Skip and Limit\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\", \"cool_person\").skip(1).limit(1).all()\n\t\t`,\n\t\texpect: []string{\"<dani>\"},\n\t},\n\n\t{\n\t\tmessage: \"show Count\",\n\t\tquery: `\n\t\t\t\tg.V().has(\"<status>\").count()\n\t\t`,\n\t\texpect: []string{\"5\"},\n\t},\n\t{\n\t\tmessage: \"use Count value\",\n\t\tquery: `\n\t\t\t\tg.emit(g.V().has(\"<status>\").count()+1)\n\t\t`,\n\t\texpect: []string{\"6\"},\n\t},\n\n\t// Tag tests.\n\t{\n\t\tmessage: \"show a simple save\",\n\t\tquery: `\n\t\t\tg.V().save(\"<status>\", \"somecool\").all()\n\t\t`,\n\t\ttag:    \"somecool\",\n\t\texpect: []string{\"cool_person\", \"cool_person\", \"cool_person\", \"smart_person\", \"smart_person\"},\n\t},\n\t{\n\t\tmessage: \"show a simple save optional\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\",\"<charlie>\").out(\"<follows>\").saveOpt(\"<status>\", \"somecool\").all()\n\t\t`,\n\t\ttag:    \"somecool\",\n\t\texpect: []string{\"cool_person\", \"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"save iri no tag\",\n\t\tquery: `\n\t\t\tg.V().save(g.IRI(\"status\")).all()\n\t\t`,\n\t\ttag:    \"<status>\",\n\t\texpect: []string{\"cool_person\", \"cool_person\", \"cool_person\", \"smart_person\", \"smart_person\"},\n\t},\n\t{\n\t\tmessage: \"show a simple saveR\",\n\t\tquery: `\n\t\t\tg.V(\"cool_person\").saveR(\"<status>\", \"who\").all()\n\t\t`,\n\t\ttag:    \"who\",\n\t\texpect: []string{\"<greg>\", \"<dani>\", \"<bob>\"},\n\t},\n\t{\n\t\tmessage: \"show an out save\",\n\t\tquery: `\n\t\t\tg.V(\"<dani>\").out(null, \"pred\").all()\n\t\t`,\n\t\ttag:    \"pred\",\n\t\texpect: []string{\"<follows>\", \"<follows>\", \"<status>\"},\n\t},\n\t{\n\t\tmessage: \"show a tag list\",\n\t\tquery: `\n\t\t\tg.V(\"<dani>\").out(null, [\"pred\", \"foo\", \"bar\"]).all()\n\t\t`,\n\t\ttag:    \"foo\",\n\t\texpect: []string{\"<follows>\", \"<follows>\", \"<status>\"},\n\t},\n\t{\n\t\tmessage: \"show a pred list\",\n\t\tquery: `\n\t\t\tg.V(\"<dani>\").out([\"<follows>\", \"<status>\"]).all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<greg>\", \"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"show a predicate path\",\n\t\tquery: `\n\t\t\tg.V(\"<dani>\").out(g.V(\"<follows>\"), \"pred\").all()\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<greg>\"},\n\t},\n\t{\n\t\tmessage: \"list all bob's incoming predicates\",\n\t\tquery: `\n\t\t  g.V(\"<bob>\").inPredicates().all()\n\t\t`,\n\t\texpect: []string{\"<follows>\"},\n\t},\n\t{\n\t\tmessage: \"save all bob's incoming predicates\",\n\t\tquery: `\n\t\t  g.V(\"<bob>\").saveInPredicates(\"pred\").all()\n\t\t`,\n\t\texpect: []string{\"<follows>\", \"<follows>\", \"<follows>\"},\n\t\ttag:    \"pred\",\n\t},\n\t{\n\t\tmessage: \"list all labels\",\n\t\tquery: `\n\t\t  g.V().labels().all()\n\t\t`,\n\t\texpect: []string{\"<smart_graph>\"},\n\t},\n\t{\n\t\tmessage: \"list all in predicates\",\n\t\tquery: `\n\t\t  g.V().inPredicates().all()\n\t\t`,\n\t\texpect: []string{\"<are>\", \"<follows>\", \"<status>\"},\n\t},\n\t{\n\t\tmessage: \"list all out predicates\",\n\t\tquery: `\n\t\t  g.V().outPredicates().all()\n\t\t`,\n\t\texpect: []string{\"<are>\", \"<follows>\", \"<status>\"},\n\t},\n\t{\n\t\tmessage: \"traverse using LabelContext\",\n\t\tquery: `\n\t\t\tg.V(\"<greg>\").labelContext(\"<smart_graph>\").out(\"<status>\").all()\n\t\t`,\n\t\texpect: []string{\"smart_person\"},\n\t},\n\t{\n\t\tmessage: \"open and close a LabelContext\",\n\t\tquery: `\n\t\t\tg.V().labelContext(\"<smart_graph>\").in(\"<status>\").labelContext(null).in(\"<follows>\").all()\n\t\t`,\n\t\texpect: []string{\"<dani>\", \"<fred>\"},\n\t},\n\t{\n\t\tmessage: \"issue #254\",\n\t\tquery:   `g.V({\"id\":\"<alice>\"}).all()`,\n\t\texpect:  nil, err: true,\n\t},\n\t{\n\t\tmessage: \"roundtrip values\",\n\t\tquery: `\n\t\tv = g.V(\"<bob>\").toValue()\n\t\ts = g.V(v).out(\"<status>\").toValue()\n\t\tg.V(s).all()\n\t\t`,\n\t\texpect: []string{\"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"roundtrip values (tag map)\",\n\t\tquery: `\n\t\tv = g.V(\"<bob>\").tagValue()\n\t\ts = g.V(v.id).out(\"<status>\").tagValue()\n\t\tg.V(s.id).all()\n\t\t`,\n\t\texpect: []string{\"cool_person\"},\n\t},\n\t{\n\t\tmessage: \"show ToArray\",\n\t\tquery: `\n\t\t\tarr = g.V(\"<bob>\").in(\"<follows>\").toArray()\n\t\t\tfor (i in arr) g.emit(arr[i]);\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"show ToArray with limit\",\n\t\tquery: `\n\t\t\tarr = g.V(\"<bob>\").in(\"<follows>\").toArray(2)\n\t\t\tfor (i in arr) g.emit(arr[i]);\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"show ForEach\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").forEach(function(o){g.emit(o.id)});\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<charlie>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"show ForEach with limit\",\n\t\tquery: `\n\t\t\tg.V(\"<bob>\").in(\"<follows>\").forEach(2, function(o){g.emit(o.id)});\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<dani>\"},\n\t},\n\t{\n\t\tmessage: \"clone paths\",\n\t\tquery: `\n\t\t\tvar alice = g.V('<alice>')\n\t\t\tg.emit(alice.toValue())\n\t\t\tvar out = alice.out('<follows>')\n\t\t\tg.emit(out.toValue())\n\t\t\tg.emit(alice.toValue())\n\t\t`,\n\t\texpect: []string{\"<alice>\", \"<bob>\", \"<alice>\"},\n\t},\n\t{\n\t\tmessage: \"default namespaces\",\n\t\tquery: `\n\t\t\tg.addDefaultNamespaces()\n\t\t\tg.emit(g.IRI('rdf:type'))\n\t\t`,\n\t\texpect: []string{\"<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>\"},\n\t},\n\t{\n\t\tmessage: \"add namespace\",\n\t\tquery: `\n\t\t\tg.addNamespace('ex','http://example.net/')\n\t\t\tg.emit(g.IRI('ex:alice'))\n\t\t`,\n\t\texpect: []string{\"<http://example.net/alice>\"},\n\t},\n\t{\n\t\tmessage: \"recursive follow\",\n\t\tquery: `\n\t\t\tg.V(\"<charlie>\").followRecursive(\"<follows>\").all();\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<dani>\", \"<fred>\", \"<greg>\"},\n\t},\n\t{\n\t\tmessage: \"recursive follow tag\",\n\t\tquery: `\n\t\t\tg.V(\"<charlie>\").followRecursive(\"<follows>\", \"depth\").all();\n\t\t`,\n\t\ttag:    \"depth\",\n\t\texpect: []string{intVal(1), intVal(1), intVal(2), intVal(2)},\n\t},\n\t{\n\t\tmessage: \"recursive follow path\",\n\t\tquery: `\n\t\t\tg.V(\"<charlie>\").followRecursive(g.V().out(\"<follows>\")).all();\n\t\t`,\n\t\texpect: []string{\"<bob>\", \"<dani>\", \"<fred>\", \"<greg>\"},\n\t},\n\t{\n\t\tmessage: \"find non-existent\",\n\t\tquery: `\n\t\t\tg.V('<not-existing>').forEach(function(d){ g.emit(d); })\n\t\t`,\n\t\texpect: nil,\n\t},\n\t{\n\t\tmessage: \"default limit All\",\n\t\tquery: `\n\t\t\tg.V().all()\n\t\t`,\n\t\tlimit:  issue718Limit,\n\t\tdata:   issue718Graph(),\n\t\texpect: issue718Nodes(),\n\t},\n\t{\n\t\tmessage: \"issue #758. Verify saveOpt respects label context\",\n\t\tquery: `\n\t\t\tg.V(\"<greg>\").labelContext(\"<smart_graph>\").saveOpt(\"<status>\", \"statusTag\").all()\n\t\t`,\n\t\ttag:    \"statusTag\",\n\t\tfile:   multiGraphTestFile,\n\t\texpect: []string{\"smart_person\"},\n\t},\n\t{\n\t\tmessage: \"issue #758. Verify saveR respects label context.\",\n\t\tquery: `\n\t\t\tg.V(\"smart_person\").labelContext(\"<other_graph>\").saveR(\"<status>\", \"who\").all()\n\t\t`,\n\t\ttag:    \"who\",\n\t\tfile:   multiGraphTestFile,\n\t\texpect: []string{\"<fred>\"},\n\t},\n\t{\n\t\tmessage: \"use order\",\n\t\tquery: `\n\t\t\tg.V().order().all()\n\t\t`,\n\t\texpect: []string{\n\t\t\t\"<alice>\",\n\t\t\t\"<are>\",\n\t\t\t\"<bob>\",\n\t\t\t\"<charlie>\",\n\t\t\t\"<dani>\",\n\t\t\t\"<emily>\",\n\t\t\t\"<follows>\",\n\t\t\t\"<fred>\",\n\t\t\t\"<greg>\",\n\t\t\t\"<predicates>\",\n\t\t\t\"<smart_graph>\",\n\t\t\t\"<status>\",\n\t\t\t\"cool_person\",\n\t\t\t\"smart_person\",\n\t\t},\n\t},\n\t{\n\t\tmessage: \"use order tags\",\n\t\tquery: `\n\t\t\tg.V().Tag(\"target\").order().all()\n\t\t`,\n\t\ttag: \"target\",\n\t\texpect: []string{\n\t\t\t\"<alice>\",\n\t\t\t\"<are>\",\n\t\t\t\"<bob>\",\n\t\t\t\"<charlie>\",\n\t\t\t\"<dani>\",\n\t\t\t\"<emily>\",\n\t\t\t\"<follows>\",\n\t\t\t\"<fred>\",\n\t\t\t\"<greg>\",\n\t\t\t\"<predicates>\",\n\t\t\t\"<smart_graph>\",\n\t\t\t\"<status>\",\n\t\t\t\"cool_person\",\n\t\t\t\"smart_person\",\n\t\t},\n\t},\n}\n\nfunc runQueryGetTag(rec func(), g []quad.Quad, qu string, tag string, limit int) ([]string, error) {\n\tjs := makeTestSession(g)\n\tctx := context.TODO()\n\tit, err := js.Execute(ctx, qu, query.Options{\n\t\tCollation: query.Raw,\n\t\tLimit:     limit,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer it.Close()\n\tdefer rec()\n\n\tvar results []string\n\tfor it.Next(ctx) {\n\t\tdata := it.Result().(*Result)\n\t\tif data.Val == nil {\n\t\t\tif val := data.Tags[tag]; val != nil {\n\t\t\t\tnv, err := js.qs.NameOf(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tresults = append(results, quadValueToString(nv))\n\t\t\t}\n\t\t} else {\n\t\t\tswitch v := data.Val.(type) {\n\t\t\tcase string:\n\t\t\t\tresults = append(results, v)\n\t\t\tdefault:\n\t\t\t\tresults = append(results, fmt.Sprint(v))\n\t\t\t}\n\t\t}\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn results, err\n\t}\n\treturn results, nil\n}\n\nfunc TestGizmo(t *testing.T) {\n\n\tsimpleGraph := testutil.LoadGraph(t, \"../../data/testdata.nq\")\n\tmultiGraph := testutil.LoadGraph(t, multiGraphTestFile)\n\n\tfor _, test := range testQueries {\n\t\ttest := test\n\t\tt.Run(test.message, func(t *testing.T) {\n\t\t\trec := func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected panic on %s: %v\", test.message, r)\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefer rec()\n\t\t\tif test.tag == \"\" {\n\t\t\t\ttest.tag = TopResultTag\n\t\t\t}\n\t\t\tquads := simpleGraph\n\t\t\tif test.file == multiGraphTestFile {\n\t\t\t\tquads = multiGraph\n\t\t\t}\n\n\t\t\tif test.data != nil {\n\t\t\t\tquads = test.data\n\t\t\t}\n\t\t\tlimit := test.limit\n\t\t\tif limit == 0 {\n\t\t\t\tlimit = -1\n\t\t\t}\n\t\t\tgot, err := runQueryGetTag(rec, quads, test.query, test.tag, limit)\n\t\t\tif err != nil {\n\t\t\t\tif test.err {\n\t\t\t\t\treturn //expected\n\t\t\t\t}\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tsort.Strings(got)\n\t\t\tsort.Strings(test.expect)\n\t\t\tif !reflect.DeepEqual(got, test.expect) {\n\t\t\t\tt.Errorf(\"got: %v expected: %v\", got, test.expect)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar issue160TestGraph = []quad.Quad{\n\tquad.MakeRaw(\"alice\", \"follows\", \"bob\", \"\"),\n\tquad.MakeRaw(\"bob\", \"follows\", \"alice\", \"\"),\n\tquad.MakeRaw(\"charlie\", \"follows\", \"bob\", \"\"),\n\tquad.MakeRaw(\"dani\", \"follows\", \"charlie\", \"\"),\n\tquad.MakeRaw(\"dani\", \"follows\", \"alice\", \"\"),\n\tquad.MakeRaw(\"alice\", \"is\", \"cool\", \"\"),\n\tquad.MakeRaw(\"bob\", \"is\", \"not cool\", \"\"),\n\tquad.MakeRaw(\"charlie\", \"is\", \"cool\", \"\"),\n\tquad.MakeRaw(\"danie\", \"is\", \"not cool\", \"\"),\n}\n\nfunc TestIssue160(t *testing.T) {\n\tqu := `g.V().tag('query').out(raw('follows')).out(raw('follows')).forEach(function (item) {\n\t\tif (item.id !== item.query) g.emit({ id: item.id });\n\t})`\n\texpect := []string{\n\t\t\"****\\nid : alice\\n\",\n\t\t\"****\\nid : bob\\n\",\n\t\t\"****\\nid : bob\\n\",\n\t}\n\n\tses := makeTestSession(issue160TestGraph)\n\tctx := context.TODO()\n\tit, err := ses.Execute(ctx, qu, query.Options{\n\t\tCollation: query.REPL,\n\t\tLimit:     100,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer it.Close()\n\tvar got []string\n\tfor it.Next(ctx) {\n\t\tfunc() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected panic: %v\", r)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tgot = append(got, it.Result().(string))\n\t\t}()\n\t}\n\tsort.Strings(got)\n\tif !reflect.DeepEqual(got, expect) {\n\t\tt.Errorf(\"Unexpected result, got: %q expected: %q\", got, expect)\n\t}\n}\n\nconst issue718Limit = 5\n\nfunc issue718Graph() []quad.Quad {\n\tvar quads []quad.Quad\n\tfor i := 0; i < issue718Limit; i++ {\n\t\tn := fmt.Sprintf(\"n%d\", i+1)\n\t\tquads = append(quads, quad.MakeIRI(\"a\", \"b\", n, \"\"))\n\t}\n\treturn quads\n}\n\nfunc issue718Nodes() []string {\n\tvar nodes []string\n\tnodes = append(nodes, \"<a>\", \"<b>\")\n\tfor i := 0; i < issue718Limit-2; i++ {\n\t\tn := fmt.Sprintf(\"<n%d>\", i+1)\n\t\tnodes = append(nodes, n)\n\t}\n\treturn nodes\n}\n"
  },
  {
    "path": "query/gizmo/traversals.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage gizmo\n\n// Adds special traversal functions to JS Gizmo objects. Most of these just build the chain of objects, and won't often need the session.\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/dop251/goja\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// pathObject is a Path object in Gizmo.\n//\n// Both `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods.\n// Note that `.Vertex()` returns a query object, which is a subclass of path object.\n//\n// For these examples, suppose we have the following graph:\n//\n//\t+-------+                        +------+\n//\t| alice |-----                 ->| fred |<--\n//\t+-------+     \\---->+-------+-/  +------+   \\-+-------+\n//\t              ----->| #bob# |       |         |*emily*|\n//\t+---------+--/  --->+-------+       |         +-------+\n//\t| charlie |    /                    v\n//\t+---------+   /                  +--------+\n//\t  \\---    +--------+             |*#greg#*|\n//\t      \\-->| #dani# |------------>+--------+\n//\t          +--------+\n//\n// Where every link is a `<follows>` relationship, and the nodes with an extra `#` in the name have an extra `<status>` link. As in,\n//\n//\t<dani> -- <status> --> \"cool_person\"\n//\n// Perhaps these are the influencers in our community. So too are extra `*`s in the name -- these are our smart people,\n// according to the `<smart_graph>` label, eg, the quad:\n//\n//\t<greg> <status> \"smart_person\" <smart_graph> .\ntype pathObject struct {\n\ts      *Session\n\tfinals bool\n\tpath   *path.Path\n}\n\nfunc (p *pathObject) new(np *path.Path) *pathObject {\n\treturn &pathObject{\n\t\ts:      p.s,\n\t\tfinals: p.finals,\n\t\tpath:   np,\n\t}\n}\n\nfunc (p *pathObject) newVal(np *path.Path) goja.Value {\n\treturn p.s.vm.ToValue(p.new(np))\n}\nfunc (p *pathObject) clonePath() *path.Path {\n\tnp := p.path.Clone()\n\t// most likely path will be continued, so we'll put non-capped stack slice\n\t// into new path object instead of preserving it in an old one\n\tp.path, np = np, p.path\n\treturn np\n}\nfunc (p *pathObject) buildIteratorTree() iterator.Shape {\n\tif p.path == nil {\n\t\treturn iterator.NewNull()\n\t}\n\treturn p.path.BuildIteratorOn(p.s.ctx, p.s.qs)\n}\n\n// Filter all paths to ones which, at this point, are on the given node.\n// Signature: (node, [node..])\n//\n// Arguments:\n//\n// * `node`: A string for a node. Can be repeated or a list of strings.\n//\n// Example:\n//\t// javascript\n//\t// Starting from all nodes in the graph, find the paths that follow bob.\n//\t// Results in three paths for bob (from alice, charlie and dani).all()\n//\tg.V().out(\"<follows>\").is(\"<bob>\").all()\nfunc (p *pathObject) Is(call goja.FunctionCall) goja.Value {\n\targs, err := toQuadValues(exportArgs(call.Arguments))\n\tif err != nil {\n\t\treturn throwErr(p.s.vm, err)\n\t}\n\tnp := p.clonePath().Is(args...)\n\treturn p.newVal(np)\n}\nfunc (p *pathObject) inout(call goja.FunctionCall, in bool) goja.Value {\n\tpreds, tags, ok := toViaData(exportArgs(call.Arguments))\n\tif !ok {\n\t\treturn throwErr(p.s.vm, errNoVia)\n\t}\n\tnp := p.clonePath()\n\tif in {\n\t\tnp = np.InWithTags(tags, preds...)\n\t} else {\n\t\tnp = np.OutWithTags(tags, preds...)\n\t}\n\treturn p.newVal(np)\n}\n\n// In is inverse of Out.\n// Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects.\n// Signature: ([predicatePath], [tags])\n//\n// Arguments:\n//\n// * `predicatePath` (Optional): One of:\n//   * null or undefined: All predicates pointing into this node\n//   * a string: The predicate name to follow into this node\n//   * a list of strings: The predicates to follow into this node\n//   * a query path object: The target of which is a set of predicates to follow.\n// * `tags` (Optional): One of:\n//   * null or undefined: No tags\n//   * a string: A single tag to add the predicate used to the output set.\n//   * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n//\n// Example:\n//\n//\t// javascript\n//\t// Find the cool people, bob, dani and greg\n//\tg.V(\"cool_person\").in(\"<status>\").all()\n//\t// Find who follows bob, in this case, alice, charlie, and dani\n//\tg.V(\"<bob>\").in(\"<follows>\").all()\n//\t// Find who follows the people emily follows, namely, bob and emily\n//\tg.V(\"<emily>\").out(\"<follows>\").in(\"<follows>\").all()\nfunc (p *pathObject) In(call goja.FunctionCall) goja.Value {\n\treturn p.inout(call, true)\n}\n\n// Out is the work-a-day way to get between nodes, in the forward direction.\n// Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects.\n// Signature: ([predicatePath], [tags])\n//\n// Arguments:\n//\n// * `predicatePath` (Optional): One of:\n//   * null or undefined: All predicates pointing out from this node\n//   * a string: The predicate name to follow out from this node\n//   * a list of strings: The predicates to follow out from this node\n//   * a query path object: The target of which is a set of predicates to follow.\n// * `tags` (Optional): One of:\n//   * null or undefined: No tags\n//   * a string: A single tag to add the predicate used to the output set.\n//   * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n//\n// Example:\n//\n//\t// javascript\n//\t// The working set of this is bob and dani\n//\tg.V(\"<charlie>\").out(\"<follows>\").all()\n//\t// The working set of this is fred, as alice follows bob and bob follows fred.\n//\tg.V(\"<alice>\").out(\"<follows>\").out(\"<follows>\").all()\n//\t// Finds all things dani points at. Result is bob, greg and cool_person\n//\tg.V(\"<dani>\").out().all()\n//\t// Finds all things dani points at on the status linkage.\n//\t// Result is bob, greg and cool_person\n//\tg.V(\"<dani>\").out([\"<follows>\", \"<status>\"]).all()\n//\t// Finds all things dani points at on the status linkage, given from a separate query path.\n//\t// Result is {\"id\": \"cool_person\", \"pred\": \"<status>\"}\n//\tg.V(\"<dani>\").out(g.V(\"<status>\"), \"pred\").all()\nfunc (p *pathObject) Out(call goja.FunctionCall) goja.Value {\n\treturn p.inout(call, false)\n}\n\n// Both follow the predicate in either direction. Same as Out or In.\n// Signature: ([predicatePath], [tags])\n//\n// Example:\n//\t// javascript\n//\t// Find all followers/followees of fred. Returns bob, emily and greg\n//\tg.V(\"<fred>\").both(\"<follows>\").all()\nfunc (p *pathObject) Both(call goja.FunctionCall) goja.Value {\n\tpreds, tags, ok := toViaData(exportArgs(call.Arguments))\n\tif !ok {\n\t\treturn throwErr(p.s.vm, errNoVia)\n\t}\n\tnp := p.clonePath().BothWithTags(tags, preds...)\n\treturn p.newVal(np)\n}\nfunc (p *pathObject) follow(ep *pathObject, rev bool) *pathObject {\n\tif ep == nil {\n\t\treturn p\n\t}\n\tnp := p.clonePath()\n\tif rev {\n\t\tnp = np.FollowReverse(ep.path)\n\t} else {\n\t\tnp = np.Follow(ep.path)\n\t}\n\treturn p.new(np)\n}\n\n// Follow is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path.\n//\n// Starts as if at the g.M() and follows through the morphism path.\n//\n// Example:\n// \t// javascript:\n//\tvar friendOfFriend = g.Morphism().Out(\"<follows>\").Out(\"<follows>\")\n//\t// Returns the followed people of who charlie follows -- a simplistic \"friend of my friend\"\n//\t// and whether or not they have a \"cool\" status. Potential for recommending followers abounds.\n//\t// Returns bob and greg\n//\tg.V(\"<charlie>\").follow(friendOfFriend).has(\"<status>\", \"cool_person\").all()\nfunc (p *pathObject) Follow(path *pathObject) *pathObject {\n\treturn p.follow(path, false)\n}\n\n// FollowR is the same as Follow but follows the chain in the reverse direction. Flips \"In\" and \"Out\" where appropriate,\n// the net result being a virtual predicate followed in the reverse direction.\n//\n// Starts at the end of the morphism and follows it backwards (with appropriate flipped directions) to the g.M() location.\n//\n// Example:\n// \t// javascript:\n//\tvar friendOfFriend = g.Morphism().Out(\"<follows>\").Out(\"<follows>\")\n//\t// Returns the third-tier of influencers -- people who follow people who follow the cool people.\n//\t// Returns charlie (from bob), charlie (from greg), bob and emily\n//\tg.V().has(\"<status>\", \"cool_person\").followR(friendOfFriend).all()\nfunc (p *pathObject) FollowR(path *pathObject) *pathObject {\n\treturn p.follow(path, true)\n}\n\n// FollowRecursive is the same as Follow but follows the chain recursively.\n//\n// Starts as if at the g.M() and follows through the morphism path multiple times, returning all nodes encountered.\n//\n// Example:\n// \t// javascript:\n//\tvar friend = g.Morphism().out(\"<follows>\")\n//\t// Returns all people in Charlie's network.\n//\t// Returns bob and dani (from charlie), fred (from bob) and greg (from dani).\n//\tg.V(\"<charlie>\").followRecursive(friend).all()\nfunc (p *pathObject) FollowRecursive(call goja.FunctionCall) goja.Value {\n\tpreds, maxDepth, tags, ok := toViaDepthData(exportArgs(call.Arguments))\n\tif !ok || len(preds) == 0 {\n\t\treturn throwErr(p.s.vm, errNoVia)\n\t} else if len(preds) != 1 {\n\t\treturn throwErr(p.s.vm, fmt.Errorf(\"expected one predicate or path for recursive follow\"))\n\t}\n\tnp := p.clonePath()\n\tnp = np.FollowRecursive(preds[0], maxDepth, tags)\n\treturn p.newVal(np)\n}\n\n// And is an alias for Intersect.\nfunc (p *pathObject) And(path *pathObject) *pathObject {\n\treturn p.Intersect(path)\n}\n\n// Intersect filters all paths by the result of another query path.\n//\n// This is essentially a join where, at the stage of each path, a node is shared.\n// Example:\n// \t// javascript\n//\tvar cFollows = g.V(\"<charlie>\").Out(\"<follows>\")\n//\tvar dFollows = g.V(\"<dani>\").Out(\"<follows>\")\n//\t// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\n//\tcFollows.Intersect(dFollows).All()\n//\t// Equivalently, g.V(\"<charlie>\").Out(\"<follows>\").And(g.V(\"<dani>\").Out(\"<follows>\")).All()\nfunc (p *pathObject) Intersect(path *pathObject) *pathObject {\n\tif path == nil {\n\t\treturn p\n\t}\n\tnp := p.clonePath().And(path.path)\n\treturn p.new(np)\n}\n\n// Union returns the combined paths of the two queries.\n//\n// Notice that it's per-path, not per-node. Once again, if multiple paths reach the same destination,\n// they might have had different ways of getting there (and different tags).\n// See also: `path.Tag()`\n//\n// Example:\n// \t// javascript\n//\tvar cFollows = g.V(\"<charlie>\").Out(\"<follows>\")\n//\tvar dFollows = g.V(\"<dani>\").Out(\"<follows>\")\n//\t// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), dani, bob (from dani), and greg.\n//\tcFollows.Union(dFollows).All()\nfunc (p *pathObject) Union(path *pathObject) *pathObject {\n\tif path == nil {\n\t\treturn p\n\t}\n\tnp := p.clonePath().Or(path.path)\n\treturn p.new(np)\n}\n\n// Or is an alias for Union.\nfunc (p *pathObject) Or(path *pathObject) *pathObject {\n\treturn p.Union(path)\n}\n\n// Back returns current path to a set of nodes on a given tag, preserving all constraints.\n//\n// If still valid, a path will now consider their vertex to be the same one as the previously tagged one,\n// with the added constraint that it was valid all the way here.\n// Useful for traversing back in queries and taking another route for things that have matched so far.\n//\n// Arguments:\n//\n// * `tag`: A previous tag in the query to jump back to.\n//\n// Example:\n// \t// javascript\n//\t// Start from all nodes, save them into start, follow any status links,\n//\t// jump back to the starting node, and find who follows them. Return the result.\n//\t// Results are:\n//\t//   {\"id\": \"<alice>\", \"start\": \"<bob>\"},\n//\t//   {\"id\": \"<charlie>\", \"start\": \"<bob>\"},\n//\t//   {\"id\": \"<charlie>\", \"start\": \"<dani>\"},\n//\t//   {\"id\": \"<dani>\", \"start\": \"<bob>\"},\n//\t//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//\t//   {\"id\": \"<dani>\", \"start\": \"<greg>\"},\n//\t//   {\"id\": \"<fred>\", \"start\": \"<greg>\"},\n//\t//   {\"id\": \"<fred>\", \"start\": \"<greg>\"}\n//\tg.V().tag(\"start\").out(\"<status>\").back(\"start\").in(\"<follows>\").all()\nfunc (p *pathObject) Back(tag string) *pathObject {\n\tnp := p.clonePath().Back(tag)\n\treturn p.new(np)\n}\n\n// Tag saves a list of nodes to a given tag.\n//\n// In order to save your work or learn more about how a path got to the end, we have tags.\n// The simplest thing to do is to add a tag anywhere you'd like to put each node in the result set.\n//\n// Arguments:\n//\n// * `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached \"Tag\"\n// Example:\n// \t// javascript\n//\t// Start from all nodes, save them into start, follow any status links, and return the result.\n//\t// Results are:\n//\t//   {\"id\": \"cool_person\", \"start\": \"<bob>\"},\n//\t//   {\"id\": \"cool_person\", \"start\": \"<dani>\"},\n//\t//   {\"id\": \"cool_person\", \"start\": \"<greg>\"},\n//\t//   {\"id\": \"smart_person\", \"start\": \"<emily>\"},\n//\t//   {\"id\": \"smart_person\", \"start\": \"<greg>\"}\n//\tg.V().tag(\"start\").out(\"<status>\").All()\nfunc (p *pathObject) Tag(tags ...string) *pathObject {\n\tnp := p.clonePath().Tag(tags...)\n\treturn p.new(np)\n}\n\n// As is an alias for Tag.\nfunc (p *pathObject) As(tags ...string) *pathObject {\n\treturn p.Tag(tags...)\n}\n\n// Has filters all paths which are, at this point, on the subject for the given predicate and object,\n// but do not follow the path, merely filter the possible paths.\n//\n// Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\n//\n// Signature: (predicate, object)\n//\n// Arguments:\n//\n// * `predicate`: A string for a predicate node.\n// * `object`: A string for a object node or a set of filters to find it.\n//\n// Example:\n// \t// javascript\n//\t// Start from all nodes that follow bob -- results in alice, charlie and dani\n//\tg.V().has(\"<follows>\", \"<bob>\").all()\n//\t// People charlie follows who then follow fred. Results in bob.\n//\tg.V(\"<charlie>\").Out(\"<follows>\").has(\"<follows>\", \"<fred>\").all()\n//\t// People with friends who have names sorting lower then \"f\".\n//\tg.V().has(\"<follows>\", gt(\"<f>\")).all()\nfunc (p *pathObject) Has(call goja.FunctionCall) goja.Value {\n\treturn p.has(call, false)\n}\n\n// HasR is the same as Has, but sets constraint in reverse direction.\nfunc (p *pathObject) HasR(call goja.FunctionCall) goja.Value {\n\treturn p.has(call, true)\n}\nfunc (p *pathObject) has(call goja.FunctionCall, rev bool) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) == 0 {\n\t\treturn throwErr(p.s.vm, errArgCount{Got: len(args)})\n\t}\n\tvia := args[0]\n\targs = args[1:]\n\tif vp, ok := via.(*pathObject); ok {\n\t\tvia = vp.path\n\t} else {\n\t\tvar err error\n\t\tvia, err = toQuadValue(via)\n\t\tif err != nil {\n\t\t\treturn throwErr(p.s.vm, err)\n\t\t}\n\t}\n\tif len(args) > 0 {\n\t\tvar filt []shape.ValueFilter\n\tloop:\n\t\tfor _, a := range args {\n\t\t\tswitch a := a.(type) {\n\t\t\tcase valFilter:\n\t\t\t\tfilt = append(filt, a.f)\n\t\t\tcase []valFilter:\n\t\t\t\tfor _, s := range a {\n\t\t\t\t\tfilt = append(filt, s.f)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tfilt = nil\n\t\t\t\t// failed to collect all argument as filters - switch back to nodes-only mode\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t}\n\t\tif len(filt) > 0 {\n\t\t\tnp := p.clonePath()\n\t\t\tnp = np.HasFilter(via, rev, filt...)\n\t\t\treturn p.newVal(np)\n\t\t}\n\t}\n\tqv, err := toQuadValues(args)\n\tif err != nil {\n\t\treturn throwErr(p.s.vm, err)\n\t}\n\tnp := p.clonePath()\n\tif rev {\n\t\tnp = np.HasReverse(via, qv...)\n\t} else {\n\t\tnp = np.Has(via, qv...)\n\t}\n\treturn p.newVal(np)\n}\nfunc (p *pathObject) save(call goja.FunctionCall, rev, opt bool) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) > 2 || len(args) == 0 {\n\t\treturn throwErr(p.s.vm, errArgCount{Got: len(args)})\n\t}\n\tvar vtag interface{} = \"\"\n\tif len(args) == 2 {\n\t\tvtag = args[1]\n\t}\n\ttag, ok := vtag.(string)\n\tif !ok {\n\t\treturn throwErr(p.s.vm, fmt.Errorf(\"expected string, got: %T\", vtag))\n\t}\n\tvia := args[0]\n\tif vp, ok := via.(*pathObject); ok {\n\t\tvia = vp.path\n\t\tif tag == \"\" {\n\t\t\treturn throwErr(p.s.vm, errors.New(\"must specify a tag name when saving a path\"))\n\t\t}\n\t} else {\n\t\tqv, err := toQuadValue(via)\n\t\tvia = qv\n\t\tif err != nil {\n\t\t\treturn throwErr(p.s.vm, err)\n\t\t}\n\t\tif tag == \"\" {\n\t\t\tif p.s.col == query.JSONLD {\n\t\t\t\tswitch qv := qv.(type) {\n\t\t\t\tcase quad.IRI:\n\t\t\t\t\ttag = string(qv)\n\t\t\t\tcase quad.String:\n\t\t\t\t\ttag = string(qv)\n\t\t\t\tdefault:\n\t\t\t\t\ttag = quad.StringOf(qv)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttag = quad.StringOf(qv)\n\t\t\t}\n\t\t}\n\t}\n\tnp := p.clonePath()\n\tif opt {\n\t\tif rev {\n\t\t\tnp = np.SaveOptionalReverse(via, tag)\n\t\t} else {\n\t\t\tnp = np.SaveOptional(via, tag)\n\t\t}\n\t} else {\n\t\tif rev {\n\t\t\tnp = np.SaveReverse(via, tag)\n\t\t} else {\n\t\t\tnp = np.Save(via, tag)\n\t\t}\n\t}\n\treturn p.newVal(np)\n}\n\n// Save saves the object of all quads with predicate into tag, without traversal.\n// Signature: (predicate, tag)\n//\n// Arguments:\n//\n// * `predicate`: A string for a predicate node.\n// * `tag`: A string for a tag key to store the object node.\n//\n// Example:\n// \t// javascript\n//\t// Start from dani and bob and save who they follow into \"target\"\n//\t// Returns:\n//\t//   {\"id\" : \"<bob>\", \"target\": \"<fred>\" },\n//\t//   {\"id\" : \"<dani>\", \"target\": \"<bob>\" },\n//\t//   {\"id\" : \"<dani>\", \"target\": \"<greg>\" }\n//\tg.V(\"<dani>\", \"<bob>\").Save(\"<follows>\", \"target\").All()\nfunc (p *pathObject) Save(call goja.FunctionCall) goja.Value {\n\treturn p.save(call, false, false)\n}\n\n// SaveR is the same as Save, but tags values via reverse predicate.\nfunc (p *pathObject) SaveR(call goja.FunctionCall) goja.Value {\n\treturn p.save(call, true, false)\n}\n\n// SaveOpt is the same as Save, but returns empty tags if predicate does not exists.\nfunc (p *pathObject) SaveOpt(call goja.FunctionCall) goja.Value {\n\treturn p.save(call, false, true)\n}\n\n// SaveOptR is the same as SaveOpt, but tags values via reverse predicate.\nfunc (p *pathObject) SaveOptR(call goja.FunctionCall) goja.Value {\n\treturn p.save(call, true, true)\n}\n\n// Except removes all paths which match query from current path.\n//\n// In a set-theoretic sense, this is (A - B). While `g.V().Except(path)` to achieve `U - B = !B` is supported, it's often very slow.\n// Example:\n// \t// javascript\n//\tvar cFollows = g.V(\"<charlie>\").Out(\"<follows>\")\n//\tvar dFollows = g.V(\"<dani>\").Out(\"<follows>\")\n//\t// People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob.\n//\tcFollows.Except(dFollows).All()   // The set (dani) -- what charlie follows that dani does not also follow.\n//\t// Equivalently, g.V(\"<charlie>\").Out(\"<follows>\").Except(g.V(\"<dani>\").Out(\"<follows>\")).All()\nfunc (p *pathObject) Except(path *pathObject) *pathObject {\n\tif path == nil {\n\t\treturn p\n\t}\n\tnp := p.clonePath().Except(path.path)\n\treturn p.new(np)\n}\n\n// Unique removes duplicate values from the path.\nfunc (p *pathObject) Unique() *pathObject {\n\tnp := p.clonePath().Unique()\n\treturn p.new(np)\n}\n\n// Difference is an alias for Except.\nfunc (p *pathObject) Difference(path *pathObject) *pathObject {\n\treturn p.Except(path)\n}\n\n// Labels gets the list of inbound and outbound quad labels\nfunc (p *pathObject) Labels() *pathObject {\n\tnp := p.clonePath().Labels()\n\treturn p.new(np)\n}\n\n// InPredicates gets the list of predicates that are pointing in to a node.\n//\n// Example:\n// \t// javascript\n//\t// bob only has \"<follows>\" predicates pointing inward\n//\t// returns \"<follows>\"\n//\tg.V(\"<bob>\").InPredicates().All()\nfunc (p *pathObject) InPredicates() *pathObject {\n\tnp := p.clonePath().InPredicates()\n\treturn p.new(np)\n}\n\n// OutPredicates gets the list of predicates that are pointing out from a node.\n//\n// Example:\n// \t// javascript\n//\t// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n//\t// returns \"<follows>\", \"<status>\"\n//\tg.V(\"<bob>\").OutPredicates().All()\nfunc (p *pathObject) OutPredicates() *pathObject {\n\tnp := p.clonePath().OutPredicates()\n\treturn p.new(np)\n}\n\n// SaveInPredicates tags the list of predicates that are pointing in to a node.\n//\n// Example:\n// \t// javascript\n//\t// bob only has \"<follows>\" predicates pointing inward\n//\t// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\n//\tg.V(\"<bob>\").SaveInPredicates(\"pred\").All()\nfunc (p *pathObject) SaveInPredicates(tag string) *pathObject {\n\tnp := p.clonePath().SavePredicates(true, tag)\n\treturn p.new(np)\n}\n\n// SaveOutPredicates tags the list of predicates that are pointing out from a node.\n//\n// Example:\n// \t// javascript\n//\t// bob has \"<follows>\" and \"<status>\" edges pointing outwards\n//\t// returns {\"id\":\"<bob>\", \"pred\":\"<follows>\"}\n//\tg.V(\"<bob>\").SaveInPredicates(\"pred\").All()\nfunc (p *pathObject) SaveOutPredicates(tag string) *pathObject {\n\tnp := p.clonePath().SavePredicates(false, tag)\n\treturn p.new(np)\n}\n\n// LabelContext sets (or removes) the subgraph context to consider in the following traversals.\n// Affects all In(), Out(), and Both() calls that follow it. The default LabelContext is null (all subgraphs).\n// Signature: ([labelPath], [tags])\n//\n// Arguments:\n//\n// * `predicatePath` (Optional): One of:\n//   * null or undefined: In future traversals, consider all edges, regardless of subgraph.\n//   * a string: The name of the subgraph to restrict traversals to.\n//   * a list of strings: A set of subgraphs to restrict traversals to.\n//   * a query path object: The target of which is a set of subgraphs.\n// * `tags` (Optional): One of:\n//   * null or undefined: No tags\n//   * a string: A single tag to add the last traversed label to the output set.\n//   * a list of strings: Multiple tags to use as keys to save the label used to the output set.\n//\n// Example:\n// \t// javascript\n//\t// Find the status of people Dani follows\n//\tg.V(\"<dani>\").out(\"<follows>\").out(\"<status>\").all()\n//\t// Find only the statuses provided by the smart_graph\n//\tg.V(\"<dani>\").out(\"<follows>\").labelContext(\"<smart_graph>\").out(\"<status>\").all()\n//\t// Find all people followed by people with statuses in the smart_graph.\n//\tg.V().labelContext(\"<smart_graph>\").in(\"<status>\").labelContext(null).in(\"<follows>\").all()\nfunc (p *pathObject) LabelContext(call goja.FunctionCall) goja.Value {\n\tlabels, tags, ok := toViaData(exportArgs(call.Arguments))\n\tif !ok {\n\t\treturn throwErr(p.s.vm, errNoVia)\n\t}\n\tnp := p.clonePath().LabelContextWithTags(tags, labels...)\n\treturn p.newVal(np)\n}\n\n// Filter applies constraints to a set of nodes. Can be used to filter values by range or match strings.\nfunc (p *pathObject) Filter(args ...valFilter) (*pathObject, error) {\n\tif len(args) == 0 {\n\t\treturn nil, errArgCount{Got: len(args)}\n\t}\n\tfilt := make([]shape.ValueFilter, 0, len(args))\n\tfor _, f := range args {\n\t\tif f.f == nil {\n\t\t\treturn nil, errors.New(\"invalid argument type in filter()\")\n\t\t}\n\t\tfilt = append(filt, f.f)\n\t}\n\tnp := p.clonePath().Filters(filt...)\n\treturn p.new(np), nil\n}\n\n// Limit limits a number of nodes for current path.\n//\n// Arguments:\n//\n// * `limit`: A number of nodes to limit results to.\n//\n// Example:\n// \t// javascript\n//\t// Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie\n//\tg.V().has(\"<follows>\", \"<bob>\").limit(2).all()\nfunc (p *pathObject) Limit(limit int) *pathObject {\n\tnp := p.clonePath().Limit(int64(limit))\n\treturn p.new(np)\n}\n\n// Skip skips a number of nodes for current path.\n//\n// Arguments:\n//\n// * `offset`: A number of nodes to skip.\n//\n// Example:\n//\t// javascript\n//\t// Start from all nodes that follow bob, and skip 2 nodes -- results in dani\n//\tg.V().has(\"<follows>\", \"<bob>\").skip(2).all()\nfunc (p *pathObject) Skip(offset int) *pathObject {\n\tnp := p.clonePath().Skip(int64(offset))\n\treturn p.new(np)\n}\n\nfunc (p *pathObject) Order() *pathObject {\n\tnp := p.clonePath().Order()\n\treturn p.new(np)\n}\n\n// Backwards compatibility\nfunc (p *pathObject) CapitalizedIs(call goja.FunctionCall) goja.Value {\n\treturn p.Is(call)\n}\nfunc (p *pathObject) CapitalizedIn(call goja.FunctionCall) goja.Value {\n\treturn p.In(call)\n}\nfunc (p *pathObject) CapitalizedOut(call goja.FunctionCall) goja.Value {\n\treturn p.Out(call)\n}\nfunc (p *pathObject) CapitalizedBoth(call goja.FunctionCall) goja.Value {\n\treturn p.Both(call)\n}\nfunc (p *pathObject) CapitalizedFollow(path *pathObject) *pathObject {\n\treturn p.Follow(path)\n}\nfunc (p *pathObject) CapitalizedFollowR(path *pathObject) *pathObject {\n\treturn p.FollowR(path)\n}\nfunc (p *pathObject) CapitalizedFollowRecursive(call goja.FunctionCall) goja.Value {\n\treturn p.FollowRecursive(call)\n}\nfunc (p *pathObject) CapitalizedAnd(path *pathObject) *pathObject {\n\treturn p.And(path)\n}\nfunc (p *pathObject) CapitalizedIntersect(path *pathObject) *pathObject {\n\treturn p.Intersect(path)\n}\nfunc (p *pathObject) CapitalizedUnion(path *pathObject) *pathObject {\n\treturn p.Union(path)\n}\nfunc (p *pathObject) CapitalizedOr(path *pathObject) *pathObject {\n\treturn p.Or(path)\n}\nfunc (p *pathObject) CapitalizedBack(tag string) *pathObject {\n\treturn p.Back(tag)\n}\nfunc (p *pathObject) CapitalizedTag(tags ...string) *pathObject {\n\treturn p.Tag(tags...)\n}\nfunc (p *pathObject) CapitalizedAs(tags ...string) *pathObject {\n\treturn p.As(tags...)\n}\nfunc (p *pathObject) CapitalizedHas(call goja.FunctionCall) goja.Value {\n\treturn p.Has(call)\n}\nfunc (p *pathObject) CapitalizedHasR(call goja.FunctionCall) goja.Value {\n\treturn p.HasR(call)\n}\nfunc (p *pathObject) CapitalizedSave(call goja.FunctionCall) goja.Value {\n\treturn p.Save(call)\n}\nfunc (p *pathObject) CapitalizedSaveR(call goja.FunctionCall) goja.Value {\n\treturn p.SaveR(call)\n}\nfunc (p *pathObject) CapitalizedSaveOpt(call goja.FunctionCall) goja.Value {\n\treturn p.SaveOpt(call)\n}\nfunc (p *pathObject) CapitalizedSaveOptR(call goja.FunctionCall) goja.Value {\n\treturn p.SaveOptR(call)\n}\nfunc (p *pathObject) CapitalizedExcept(path *pathObject) *pathObject {\n\treturn p.Except(path)\n}\nfunc (p *pathObject) CapitalizedUnique() *pathObject {\n\treturn p.Unique()\n}\nfunc (p *pathObject) CapitalizedDifference(path *pathObject) *pathObject {\n\treturn p.Difference(path)\n}\nfunc (p *pathObject) CapitalizedLabels() *pathObject {\n\treturn p.Labels()\n}\nfunc (p *pathObject) CapitalizedInPredicates() *pathObject {\n\treturn p.InPredicates()\n}\nfunc (p *pathObject) CapitalizedOutPredicates() *pathObject {\n\treturn p.OutPredicates()\n}\nfunc (p *pathObject) CapitalizedSaveInPredicates(tag string) *pathObject {\n\treturn p.SaveInPredicates(tag)\n}\nfunc (p *pathObject) CapitalizedSaveOutPredicates(tag string) *pathObject {\n\treturn p.SaveOutPredicates(tag)\n}\nfunc (p *pathObject) CapitalizedLabelContext(call goja.FunctionCall) goja.Value {\n\treturn p.LabelContext(call)\n}\nfunc (p *pathObject) CapitalizedFilter(args ...valFilter) (*pathObject, error) {\n\treturn p.Filter(args...)\n}\nfunc (p *pathObject) CapitalizedLimit(limit int) *pathObject {\n\treturn p.Limit(limit)\n}\nfunc (p *pathObject) CapitalizedSkip(offset int) *pathObject {\n\treturn p.Skip(offset)\n}\n"
  },
  {
    "path": "query/graphql/graphql.go",
    "content": "package graphql\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/dennwc/graphql/language/ast\"\n\t\"github.com/dennwc/graphql/language/lexer\"\n\t\"github.com/dennwc/graphql/language/parser\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nconst Name = \"graphql\"\n\n// GraphQL charset: [_A-Za-z][_0-9A-Za-z]*\n// (https://facebook.github.io/graphql/#sec-Names)\n\n// IRI charset: [^#x00-#x20<>\"{}|^`\\]\n// (https://www.w3.org/TR/turtle/#grammar-production-IRIREF)\n\nfunc allowedNameRune(r rune) bool {\n\t// will include <> in the IRI value\n\treturn r > 0x20 && !strings.ContainsRune(\"\\\"{}()|^`\", r) && !unicode.IsSpace(r)\n}\n\nfunc init() {\n\tlexer.AllowNameRunes = allowedNameRune\n\n\tquery.RegisterLanguage(query.Language{\n\t\tName: Name,\n\t\tSession: func(qs graph.QuadStore) query.Session {\n\t\t\treturn NewSession(qs)\n\t\t},\n\t\tHTTPError: httpError,\n\t\tHTTPQuery: httpQuery,\n\t})\n}\n\nfunc NewSession(qs graph.QuadStore) *Session {\n\treturn &Session{qs: qs}\n}\n\ntype Session struct {\n\tqs graph.QuadStore\n}\n\nfunc (s *Session) Execute(ctx context.Context, qu string, opt query.Options) (query.Iterator, error) {\n\tswitch opt.Collation {\n\tcase query.Raw, query.JSON, query.REPL:\n\tdefault:\n\t\treturn nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}\n\t}\n\tq, err := Parse(strings.NewReader(qu))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &results{\n\t\ts:   s,\n\t\tq:   q,\n\t\tcol: opt.Collation,\n\t}, nil\n}\n\ntype results struct {\n\ts   *Session\n\tq   *Query\n\tcol query.Collation\n\tres map[string]interface{}\n\terr error\n}\n\nfunc (it *results) Next(ctx context.Context) bool {\n\tif it.q == nil {\n\t\treturn false\n\t}\n\tit.res, it.err = it.q.Execute(ctx, it.s.qs)\n\tit.q = nil\n\treturn it.err == nil && len(it.res) != 0\n}\n\nfunc (it *results) Result() interface{} {\n\tif len(it.res) == 0 {\n\t\treturn nil\n\t}\n\tif it.col != query.REPL {\n\t\treturn it.res\n\t}\n\tdata, _ := json.MarshalIndent(it.res, \"\", \"   \")\n\treturn string(data)\n}\n\nfunc (it *results) Err() error {\n\treturn it.err\n}\n\nfunc (it *results) Close() error {\n\tit.q = nil\n\treturn nil\n}\n\n// Configurable keywords and special field names.\nvar (\n\tValueKey = \"id\"\n\tLimitKey = \"first\"\n\tSkipKey  = \"offset\"\n\tAnyKey   = \"*\"\n)\n\ntype Query struct {\n\tfields []field\n}\n\ntype has struct {\n\tVia    quad.IRI\n\tRev    bool\n\tValues []quad.Value\n\tLabels []quad.Value\n}\n\ntype field struct {\n\tVia       quad.IRI\n\tAlias     string\n\tRev       bool\n\tOpt       bool\n\tLabels    []quad.Value\n\tHas       []has\n\tFields    []field\n\tAllFields bool // fetch all fields\n\tUnNest    bool // all fields will be saved to parent object\n}\n\nfunc (f field) isSave() bool { return len(f.Has)+len(f.Fields) == 0 && !f.AllFields }\n\ntype object struct {\n\tid     graph.Ref\n\tfields map[string]interface{}\n}\n\nfunc buildIterator(ctx context.Context, qs graph.QuadStore, p *path.Path) iterator.Shape {\n\tit, _ := p.BuildIterator(ctx).Optimize(ctx)\n\treturn it\n}\n\nfunc iterateObject(ctx context.Context, qs graph.QuadStore, f *field, p *path.Path) (out []map[string]interface{}, _ error) {\n\tif len(f.Labels) != 0 {\n\t\tp = p.LabelContext(f.Labels)\n\t} else {\n\t\tp = p.LabelContext()\n\t}\n\tvar (\n\t\tlimit = -1\n\t\tskip  = 0\n\t)\n\n\tfor _, h := range f.Has {\n\t\tswitch h.Via {\n\t\tcase quad.IRI(ValueKey): // special key - \"id\"\n\t\t\tp = p.Is(h.Values...)\n\t\tcase quad.IRI(LimitKey), quad.IRI(SkipKey): // limit and skip\n\t\t\tif len(h.Values) != 1 {\n\t\t\t\treturn nil, fmt.Errorf(\"unexpected arguments: %v (%d)\", h.Values, len(h.Values))\n\t\t\t}\n\t\t\tn, ok := h.Values[0].(quad.Int)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"unexpected value type for %v: %T\", string(h.Via), h.Values[0])\n\t\t\t}\n\t\t\tif h.Via == quad.IRI(LimitKey) {\n\t\t\t\tlimit = int(n)\n\t\t\t} else {\n\t\t\t\tskip = int(n)\n\t\t\t\tif skip < 0 {\n\t\t\t\t\tskip = 0\n\t\t\t\t}\n\t\t\t}\n\t\tdefault: // everything else - Has constraint\n\t\t\tif len(h.Labels) != 0 {\n\t\t\t\tp = p.LabelContext(h.Labels)\n\t\t\t}\n\t\t\tif h.Rev {\n\t\t\t\tp = p.HasReverse(h.Via, h.Values...)\n\t\t\t} else {\n\t\t\t\tp = p.Has(h.Via, h.Values...)\n\t\t\t}\n\t\t\tif len(h.Labels) != 0 {\n\t\t\t\tp = p.LabelContext()\n\t\t\t}\n\t\t}\n\t}\n\ttail := func() {\n\t\tif skip > 0 {\n\t\t\tp = p.Skip(int64(skip))\n\t\t}\n\t\tif limit >= 0 {\n\t\t\tp = p.Limit(int64(limit))\n\t\t}\n\t}\n\tif f.AllFields {\n\t\ttail()\n\n\t\tit := buildIterator(ctx, qs, p).Iterate()\n\t\tdefer it.Close()\n\n\t\t// we don't care about alternative paths to nodes here, so we will not call NextPath\n\t\t// and we haven't tagged anything, so we will not call TagResult either\n\t\tfor i := 0; limit < 0 || i < limit; i++ {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn out, ctx.Err()\n\t\t\tdefault:\n\t\t\t}\n\t\t\tif !it.Next(ctx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tnv := it.Result()\n\t\t\tobj := make(map[string]interface{})\n\t\t\tvar err error\n\t\t\tobj[ValueKey], err = qs.NameOf(nv)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tsit := qs.QuadIterator(quad.Subject, nv).Iterate()\n\t\t\tfor sit.Next(ctx) {\n\t\t\t\tq, err := qs.Quad(sit.Result())\n\t\t\t\tif err != nil {\n\t\t\t\t\tsit.Close()\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tif p, ok := q.Predicate.(quad.IRI); ok {\n\t\t\t\t\tobj[string(p)] = q.Object\n\t\t\t\t} else {\n\t\t\t\t\tobj[quad.ToString(q.Predicate)] = q.Object\n\t\t\t\t}\n\t\t\t}\n\t\t\tsit.Close()\n\t\t\tout = append(out, obj)\n\t\t}\n\t\treturn out, it.Err()\n\t}\n\tunnest := make(map[string]bool)\n\tfor _, f2 := range f.Fields {\n\t\tif f2.UnNest {\n\t\t\tunnest[f2.Alias] = true\n\t\t}\n\t\tif !f2.isSave() {\n\t\t\tcontinue\n\t\t}\n\t\tif f2.Via == quad.IRI(ValueKey) {\n\t\t\tp = p.Tag(f2.Alias)\n\t\t\tcontinue\n\t\t}\n\t\tif len(f2.Labels) != 0 {\n\t\t\tp = p.LabelContext(f2.Labels)\n\t\t}\n\t\tif f2.Opt {\n\t\t\tif f2.Rev {\n\t\t\t\tp = p.SaveOptionalReverse(f2.Via, f2.Alias)\n\t\t\t} else {\n\t\t\t\tp = p.SaveOptional(f2.Via, f2.Alias)\n\t\t\t}\n\t\t} else {\n\t\t\tif f2.Rev {\n\t\t\t\tp = p.SaveReverse(f2.Via, f2.Alias)\n\t\t\t} else {\n\t\t\t\tp = p.Save(f2.Via, f2.Alias)\n\t\t\t}\n\t\t}\n\t\tif len(f2.Labels) != 0 {\n\t\t\tp = p.LabelContext()\n\t\t}\n\t}\n\ttail()\n\n\t// first, collect result node ids and any tags associated with it (flat values)\n\tit := buildIterator(ctx, qs, p).Iterate()\n\tdefer it.Close()\n\n\tvar results []object\n\tfor i := 0; limit < 0 || i < limit; i++ {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn out, ctx.Err()\n\t\tdefault:\n\t\t}\n\t\tif !it.Next(ctx) {\n\t\t\tbreak\n\t\t}\n\t\tfields := make(map[string][]graph.Ref)\n\n\t\ttags := make(map[string]graph.Ref)\n\t\tit.TagResults(tags)\n\t\tfor k, v := range tags {\n\t\t\tfields[k] = []graph.Ref{v}\n\t\t}\n\t\tfor it.NextPath(ctx) {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn out, ctx.Err()\n\t\t\tdefault:\n\t\t\t}\n\t\t\ttags = make(map[string]graph.Ref)\n\t\t\tit.TagResults(tags)\n\t\tdedup:\n\t\t\tfor k, v := range tags {\n\t\t\t\tvals := fields[k]\n\t\t\t\tfor _, v2 := range vals {\n\t\t\t\t\tif refs.ToKey(v) == refs.ToKey(v2) {\n\t\t\t\t\t\tcontinue dedup\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfields[k] = append(vals, v)\n\t\t\t}\n\t\t}\n\t\tobj := object{id: it.Result()}\n\t\tif len(fields) > 0 {\n\t\t\tobj.fields = make(map[string]interface{}, len(fields))\n\t\t\tfor k, arr := range fields {\n\t\t\t\tvals, err := graph.ValuesOf(ctx, qs, arr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tif len(vals) == 1 {\n\t\t\t\t\tobj.fields[k] = vals[0]\n\t\t\t\t} else {\n\t\t\t\t\tobj.fields[k] = vals\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tresults = append(results, obj)\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn out, err\n\t}\n\n\t// next, load complex objects inside fields\n\tfor _, r := range results {\n\t\tobj := r.fields\n\t\tif obj == nil {\n\t\t\tobj = make(map[string]interface{})\n\t\t}\n\t\tfor _, f2 := range f.Fields {\n\t\t\tif f2.isSave() {\n\t\t\t\tcontinue // skip flat values\n\t\t\t}\n\t\t\t// start from saved id for a field node\n\t\t\tp2 := path.StartPathNodes(qs, r.id)\n\t\t\tif len(f2.Labels) != 0 {\n\t\t\t\tp2 = p2.LabelContext(f2.Labels)\n\t\t\t}\n\t\t\tif f2.Rev {\n\t\t\t\tp2 = p2.In(f2.Via)\n\t\t\t} else {\n\t\t\t\tp2 = p2.Out(f2.Via)\n\t\t\t}\n\t\t\tif len(f2.Labels) != 0 {\n\t\t\t\tp2 = p2.LabelContext()\n\t\t\t}\n\t\t\tarr, err := iterateObject(ctx, qs, &f2, p2)\n\t\t\tif err != nil {\n\t\t\t\treturn out, err\n\t\t\t}\n\t\t\tif f2.UnNest {\n\t\t\t\tif len(arr) > 1 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"cannot unnest more than one object on %q; use (%s: 1) to force\",\n\t\t\t\t\t\tf2.Alias, LimitKey)\n\t\t\t\t} else if len(arr) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor k, v := range arr[0] {\n\t\t\t\t\tobj[k] = v\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar v interface{}\n\t\t\t\tif len(arr) == 1 {\n\t\t\t\t\tv = arr[0]\n\t\t\t\t} else if len(arr) > 1 {\n\t\t\t\t\tv = arr\n\t\t\t\t}\n\t\t\t\tobj[f2.Alias] = v\n\t\t\t}\n\t\t}\n\t\tout = append(out, obj)\n\t}\n\treturn out, nil\n}\n\nfunc (q *Query) Execute(ctx context.Context, qs graph.QuadStore) (map[string]interface{}, error) {\n\tout := make(map[string]interface{})\n\tfor _, f := range q.fields {\n\t\tarr, err := iterateObject(ctx, qs, &f, path.StartPath(qs))\n\t\tif err != nil {\n\t\t\treturn out, err\n\t\t}\n\t\tvar v interface{}\n\t\tif len(arr) == 1 {\n\t\t\tv = arr[0]\n\t\t} else if len(arr) > 1 {\n\t\t\tv = arr\n\t\t}\n\t\tout[f.Alias] = v\n\t}\n\treturn out, nil\n}\n\nfunc Parse(r io.Reader) (*Query, error) {\n\tdata, err := ioutil.ReadAll(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdoc, err := parser.Parse(parser.ParseParams{Source: string(data)})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(doc.Definitions) != 1 {\n\t\treturn nil, fmt.Errorf(\"unsupported query type\")\n\t}\n\tdef, ok := doc.Definitions[0].(*ast.OperationDefinition)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unsupported query type: %T\", doc.Definitions[0])\n\t} else if def.Operation != \"query\" {\n\t\treturn nil, fmt.Errorf(\"unsupported operation: %s\", def.Operation)\n\t}\n\tfields, all, err := setToFields(def.SelectionSet, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t} else if all {\n\t\treturn nil, fmt.Errorf(\"expand all is not supported at top level\")\n\t}\n\treturn &Query{fields: fields}, nil\n}\n\nfunc setToFields(set *ast.SelectionSet, labels []quad.Value) (out []field, all bool, _ error) {\n\tif set == nil {\n\t\treturn\n\t}\n\tfor _, s := range set.Selections {\n\t\tswitch sel := s.(type) {\n\t\tcase *ast.Field:\n\t\t\tfld, err := convField(sel, labels)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, err\n\t\t\t}\n\t\t\tif fld.Via == quad.IRI(AnyKey) {\n\t\t\t\tif len(set.Selections) != 1 {\n\t\t\t\t\treturn nil, false, fmt.Errorf(\"expand all cannot be used with other fields\")\n\t\t\t\t} else if len(fld.Has) != 0 || len(fld.Fields) != 0 {\n\t\t\t\t\treturn nil, false, fmt.Errorf(\"filters inside expand all are not supported\")\n\t\t\t\t}\n\t\t\t\treturn nil, true, nil\n\t\t\t}\n\t\t\tout = append(out, fld)\n\t\tdefault:\n\t\t\treturn nil, false, fmt.Errorf(\"unknown selection type: %T\", s)\n\t\t}\n\t}\n\treturn\n}\n\nfunc stringToVia(s string) (_ quad.IRI, rev bool) {\n\tif len(s) > 0 && s[0] == '~' {\n\t\trev = true\n\t\ts = s[1:]\n\t}\n\tif len(s) > 2 && s[0] == '<' && s[len(s)-1] == '>' {\n\t\ts = s[1 : len(s)-1]\n\t}\n\treturn quad.IRI(s), rev\n}\n\nfunc argsToHas(dst []has, args []*ast.Argument, rev bool, labels []quad.Value) (out []has, err error) {\n\tout = dst\n\tfor _, arg := range args {\n\t\tvar vals []quad.Value\n\t\tvals, err = convValue(arg.Value)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\th := has{Values: vals, Labels: labels}\n\t\th.Via, h.Rev = stringToVia(arg.Name.Value)\n\t\th.Rev = h.Rev != rev\n\t\tout = append(out, h)\n\t}\n\treturn\n}\n\nfunc convField(fld *ast.Field, labels []quad.Value) (out field, err error) {\n\tout.Labels = labels\n\tname := fld.Name.Value\n\tif fld.Alias != nil && fld.Alias.Value != \"\" {\n\t\tout.Alias = fld.Alias.Value\n\t} else {\n\t\tout.Alias = name\n\t}\n\tout.Via, out.Rev = stringToVia(name)\n\t// first check for \"label\" directive - it will affect all traversals\n\tfor _, d := range fld.Directives {\n\t\tif d.Name == nil {\n\t\t\tcontinue\n\t\t}\n\t\tswitch d.Name.Value {\n\t\tcase \"label\":\n\t\t\tif len(d.Arguments) == 0 {\n\t\t\t\tout.Labels = nil\n\t\t\t} else if len(d.Arguments) > 1 {\n\t\t\t\treturn out, fmt.Errorf(\"label directive should have 0 or 1 argument\")\n\t\t\t} else if a := d.Arguments[0]; a.Name == nil || a.Name.Value != \"v\" {\n\t\t\t\treturn out, fmt.Errorf(\"label directive should have 'v' argument\")\n\t\t\t} else {\n\t\t\t\tvals, err := convValue(a.Value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn out, fmt.Errorf(\"error parsing label: %v\", err)\n\t\t\t\t}\n\t\t\t\tout.Labels = vals\n\t\t\t}\n\t\t}\n\t}\n\tfor _, d := range fld.Directives {\n\t\tif d.Name == nil {\n\t\t\tcontinue\n\t\t}\n\t\tswitch d.Name.Value {\n\t\tcase \"rev\", \"reverse\":\n\t\t\tif len(d.Arguments) == 0 {\n\t\t\t\tout.Rev = out.Rev != true\n\t\t\t} else {\n\t\t\t\tout.Has, err = argsToHas(out.Has, d.Arguments, true, out.Labels)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"opt\", \"optional\":\n\t\t\tout.Opt = true\n\t\tcase \"label\":\n\t\t\t// already processed\n\t\tcase \"unnest\":\n\t\t\tout.UnNest = true\n\t\tdefault:\n\t\t\treturn out, fmt.Errorf(\"unknown directive: %q\", d.Name.Value)\n\t\t}\n\t}\n\tout.Fields, out.AllFields, err = setToFields(fld.SelectionSet, out.Labels)\n\tif err != nil {\n\t\treturn\n\t}\n\tout.Has, err = argsToHas(out.Has, fld.Arguments, false, out.Labels)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc convValue(v ast.Value) (out []quad.Value, _ error) {\n\tswitch v := v.(type) {\n\tcase *ast.EnumValue:\n\t\ts := v.Value\n\t\tif len(s) > 2 && s[0] == '<' && s[len(s)-1] == '>' {\n\t\t\ts = s[1 : len(s)-1]\n\t\t}\n\t\tif len(s) > 2 && s[0] == '_' && s[1] == ':' {\n\t\t\treturn []quad.Value{quad.BNode(s[2:])}, nil\n\t\t}\n\t\treturn []quad.Value{quad.IRI(s)}, nil\n\tcase *ast.StringValue:\n\t\treturn []quad.Value{quad.StringToValue(v.Value)}, nil\n\tcase *ast.IntValue:\n\t\tpv, _ := strconv.Atoi(v.Value)\n\t\treturn []quad.Value{quad.Int(pv)}, nil\n\tcase *ast.FloatValue:\n\t\tpv, _ := strconv.ParseFloat(v.Value, 64)\n\t\treturn []quad.Value{quad.Float(pv)}, nil\n\tcase *ast.BooleanValue:\n\t\treturn []quad.Value{quad.Bool(v.Value)}, nil\n\tcase *ast.ListValue:\n\t\tfor _, sv := range v.Values {\n\t\t\tcv, err := convValue(sv)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t} else if len(cv) != 1 {\n\t\t\t\treturn nil, fmt.Errorf(\"unexpected value array in list: %v (%d)\", cv, len(cv))\n\t\t\t}\n\t\t\tout = append(out, cv[0])\n\t\t}\n\t\treturn\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported value type: %T\", v)\n\t}\n}\n"
  },
  {
    "path": "query/graphql/graphql_test.go",
    "content": "package graphql\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n)\n\nfunc iris(arr ...string) (out []quad.Value) {\n\tfor _, s := range arr {\n\t\tout = append(out, quad.IRI(s))\n\t}\n\treturn\n}\n\nvar casesParse = []struct {\n\tquery  string\n\texpect []field\n}{\n\t{\n\t\t`{\n\tuser(id: 3500401, http://iri: http://some_iri, follow: <bob>, n: _:bob, v: [\"<bob>\", \"name\", 3]) @rev(follow: \"123\"){\n\tid: ` + ValueKey + `,\n\ttype: ` + rdf.NS + \"type\" + `,\n\tfollowed: follow @reverse @label(v: <fb>) {\n\t\tname: <name> @label(v: <google>)\n\t\tfollowed: ~follow\n\t\tsname @label\n\t}\n\tisViewerFriend,\n\t\tprofilePicture(size: 50) @unnest {\n\t\t\t uri,\n\t\t\t width @opt,\n\t\t\t height @rev\n\t\t}\n\tsub {*}\n\t}\n}`,\n\t\t[]field{{\n\t\t\tVia: \"user\", Alias: \"user\",\n\t\t\tHas: []has{\n\t\t\t\t{\"follow\", true, []quad.Value{quad.String(\"123\")}, nil},\n\t\t\t\t{\"id\", false, []quad.Value{quad.Int(3500401)}, nil},\n\t\t\t\t{\"http://iri\", false, iris(\"http://some_iri\"), nil},\n\t\t\t\t{\"follow\", false, iris(\"bob\"), nil},\n\t\t\t\t{\"n\", false, []quad.Value{quad.BNode(\"bob\")}, nil},\n\t\t\t\t{\"v\", false, []quad.Value{quad.IRI(\"bob\"), quad.String(\"name\"), quad.Int(3)}, nil},\n\t\t\t},\n\t\t\tFields: []field{\n\t\t\t\t{Via: quad.IRI(ValueKey), Alias: \"id\"},\n\t\t\t\t{Via: quad.IRI(\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\"), Alias: \"type\"},\n\t\t\t\t{\n\t\t\t\t\tVia: \"follow\", Alias: \"followed\", Rev: true, Labels: iris(\"fb\"),\n\t\t\t\t\tFields: []field{\n\t\t\t\t\t\t{Via: \"name\", Alias: \"name\", Labels: iris(\"google\")},\n\t\t\t\t\t\t{Via: \"follow\", Alias: \"followed\", Rev: true, Labels: iris(\"fb\")},\n\t\t\t\t\t\t{Via: \"sname\", Alias: \"sname\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{Via: \"isViewerFriend\", Alias: \"isViewerFriend\"},\n\t\t\t\t{\n\t\t\t\t\tVia: \"profilePicture\", Alias: \"profilePicture\",\n\t\t\t\t\tHas: []has{{\"size\", false, []quad.Value{quad.Int(50)}, nil}},\n\t\t\t\t\tFields: []field{\n\t\t\t\t\t\t{Via: \"uri\", Alias: \"uri\"},\n\t\t\t\t\t\t{Via: \"width\", Alias: \"width\", Opt: true},\n\t\t\t\t\t\t{Via: \"height\", Alias: \"height\", Rev: true},\n\t\t\t\t\t},\n\t\t\t\t\tUnNest: true,\n\t\t\t\t},\n\t\t\t\t{Via: \"sub\", Alias: \"sub\", AllFields: true},\n\t\t\t},\n\t\t}},\n\t},\n}\n\nfunc TestParse(t *testing.T) {\n\tfor _, c := range casesParse {\n\t\tq, err := Parse(strings.NewReader(c.query))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if !reflect.DeepEqual(q.fields, c.expect) {\n\t\t\tt.Fatalf(\"\\n%#v\\nvs\\n%#v\", q.fields, c.expect)\n\t\t}\n\t}\n}\n\ntype M = map[string]interface{}\n\nvar casesExecute = []struct {\n\tname   string\n\tquery  string\n\tresult M\n}{\n\t{\n\t\t\"cool people and friends\",\n\t\t`{\n  me(status: \"cool_person\") {\n    id: ` + ValueKey + `\n    follows {\n      ` + ValueKey + `\n      status\n    }\n    followed: follows @rev {\n      ` + ValueKey + `\n    }\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": []M{\n\t\t\t\t{\n\t\t\t\t\t\"id\":      quad.IRI(\"bob\"),\n\t\t\t\t\t\"follows\": nil,\n\t\t\t\t\t\"followed\": []M{\n\t\t\t\t\t\t{ValueKey: quad.IRI(\"alice\")},\n\t\t\t\t\t\t{ValueKey: quad.IRI(\"dani\")},\n\t\t\t\t\t\t{ValueKey: quad.IRI(\"charlie\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": quad.IRI(\"dani\"),\n\t\t\t\t\t\"follows\": []M{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tValueKey: quad.IRI(\"bob\"),\n\t\t\t\t\t\t\t\"status\": quad.String(\"cool_person\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tValueKey: quad.IRI(\"greg\"),\n\t\t\t\t\t\t\t\"status\": []quad.Value{\n\t\t\t\t\t\t\t\tquad.String(\"cool_person\"),\n\t\t\t\t\t\t\t\tquad.String(\"smart_person\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t\"followed\": M{\n\t\t\t\t\t\tValueKey: quad.IRI(\"charlie\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\":      quad.IRI(\"greg\"),\n\t\t\t\t\t\"follows\": nil,\n\t\t\t\t\t\"followed\": []M{\n\t\t\t\t\t\t{ValueKey: quad.IRI(\"dani\")},\n\t\t\t\t\t\t{ValueKey: quad.IRI(\"fred\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"skip and limit\",\n\t\t`{\n  me(status: \"cool_person\", ` + LimitKey + `: 1, ` + SkipKey + `: 1) {\n    id: ` + ValueKey + `\n    follows(` + LimitKey + `: 1) @opt {\n      ` + ValueKey + `\n    }\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": M{\n\t\t\t\t\"id\": quad.IRI(\"dani\"),\n\t\t\t\t\"follows\": M{\n\t\t\t\t\tValueKey: quad.IRI(\"bob\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"labels\",\n\t\t`{\n  me {\n    id: ` + ValueKey + `\n    status @label(v: <smart_graph>)\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": []M{\n\t\t\t\t{\n\t\t\t\t\t\"id\":     quad.IRI(\"emily\"),\n\t\t\t\t\t\"status\": quad.String(\"smart_person\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\":     quad.IRI(\"greg\"),\n\t\t\t\t\t\"status\": quad.String(\"smart_person\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"expand all\",\n\t\t`{\n  me {\n    id: ` + ValueKey + `\n    status @label(v: <smart_graph>)\n    follows {*}\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": []M{\n\t\t\t\t{\n\t\t\t\t\t\"id\":     quad.IRI(\"emily\"),\n\t\t\t\t\t\"status\": quad.String(\"smart_person\"),\n\t\t\t\t\t\"follows\": M{\n\t\t\t\t\t\t\"id\":      quad.IRI(\"fred\"),\n\t\t\t\t\t\t\"follows\": quad.IRI(\"greg\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\":      quad.IRI(\"greg\"),\n\t\t\t\t\t\"status\":  quad.String(\"smart_person\"),\n\t\t\t\t\t\"follows\": nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"unnest object\",\n\t\t`{\n  me(id: fred) {\n    id: ` + ValueKey + `\n    follows @unnest {\n      friend: ` + ValueKey + `\n      friend_status: status\n      followed: follows(` + LimitKey + `: 1) @rev @unnest  {\n        fof: ` + ValueKey + `\n      }\n    }\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": M{\n\t\t\t\t\"id\":     quad.IRI(\"fred\"),\n\t\t\t\t\"fof\":    quad.IRI(\"dani\"),\n\t\t\t\t\"friend\": quad.IRI(\"greg\"),\n\t\t\t\t\"friend_status\": []quad.Value{\n\t\t\t\t\tquad.String(\"cool_person\"),\n\t\t\t\t\tquad.String(\"smart_person\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"unnest object (non existent)\",\n\t\t`{\n  me(id: fred) {\n    id: ` + ValueKey + `\n    follows_missing @unnest {\n      friend: ` + ValueKey + `\n      friend_status: status\n    }\n  }\n}`,\n\t\tM{\n\t\t\t\"me\": M{\n\t\t\t\t\"id\": quad.IRI(\"fred\"),\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\t\"all optional\",\n\t\t`{\n  nodes {\n    id,\n    status @opt\n  }\n}`,\n\t\tM{\n\t\t\t\"nodes\": []M{\n\t\t\t\t{\"id\": quad.IRI(\"alice\")},\n\t\t\t\t{\"id\": quad.IRI(\"follows\")},\n\t\t\t\t{\"id\": quad.IRI(\"bob\"), \"status\": quad.String(\"cool_person\")},\n\t\t\t\t{\"id\": quad.IRI(\"fred\")},\n\t\t\t\t{\"id\": quad.IRI(\"status\")},\n\t\t\t\t{\"id\": quad.String(\"cool_person\")},\n\t\t\t\t{\"id\": quad.IRI(\"dani\"), \"status\": quad.String(\"cool_person\")},\n\t\t\t\t{\"id\": quad.IRI(\"charlie\")},\n\t\t\t\t{\"id\": quad.IRI(\"greg\"), \"status\": []quad.Value{\n\t\t\t\t\tquad.String(\"cool_person\"),\n\t\t\t\t\tquad.String(\"smart_person\"),\n\t\t\t\t}},\n\t\t\t\t{\"id\": quad.IRI(\"emily\"), \"status\": quad.String(\"smart_person\")},\n\t\t\t\t{\"id\": quad.IRI(\"predicates\")},\n\t\t\t\t{\"id\": quad.IRI(\"are\")},\n\t\t\t\t{\"id\": quad.String(\"smart_person\")},\n\t\t\t\t{\"id\": quad.IRI(\"smart_graph\")},\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc toJSON(o interface{}) string {\n\tbuf := bytes.NewBuffer(nil)\n\tjson.NewEncoder(buf).Encode(o)\n\tbuf2 := bytes.NewBuffer(nil)\n\tjson.Indent(buf2, buf.Bytes(), \"\", \"   \")\n\treturn buf2.String()\n}\n\nfunc TestExecute(t *testing.T) {\n\tqs := memstore.New()\n\tqw := testutil.MakeWriter(t, qs, nil)\n\tquads := testutil.LoadGraph(t, \"../../data/testdata.nq\")\n\terr := qw.AddQuadSet(quads)\n\trequire.NoError(t, err)\n\n\tfor _, c := range casesExecute {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tq, err := Parse(strings.NewReader(c.query))\n\t\t\trequire.NoError(t, err)\n\t\t\tout, err := q.Execute(context.Background(), qs)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, c.result, out, \"results:\\n%v\\n\\nvs\\n\\n%v\", toJSON(c.result), toJSON(out))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/graphql/http.go",
    "content": "package graphql\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\n\t\"github.com/dennwc/graphql/gqlerrors\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n)\n\ntype httpResult struct {\n\tData   interface{}                `json:\"data\"`\n\tErrors []gqlerrors.FormattedError `json:\"errors,omitempty\"`\n}\n\nfunc httpError(w query.ResponseWriter, err error) {\n\tjson.NewEncoder(w).Encode(httpResult{\n\t\tErrors: []gqlerrors.FormattedError{{\n\t\t\tMessage: err.Error(),\n\t\t}},\n\t})\n}\n\nfunc httpQuery(ctx context.Context, qs graph.QuadStore, w query.ResponseWriter, r io.Reader) {\n\tq, err := Parse(r)\n\tif err != nil {\n\t\thttpError(w, err)\n\t\treturn\n\t}\n\tm, err := q.Execute(ctx, qs)\n\tif err != nil {\n\t\thttpError(w, err)\n\t\treturn\n\t}\n\tjson.NewEncoder(w).Encode(httpResult{Data: m})\n}\n"
  },
  {
    "path": "query/linkedql/entity_identfier.go",
    "content": "package linkedql\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\n// EntityIdentifierI is an interface to be used where a single entity identifier is expected.\ntype EntityIdentifierI interface {\n\tBuildIdentifier(ns *voc.Namespaces) (quad.Value, error)\n}\n\n// EntityIdentifier is a struct wrapping the interface EntityIdentifierI\ntype EntityIdentifier struct {\n\tEntityIdentifierI\n}\n\n// NewEntityIdentifier constructs a new EntityIdentifer from a EntityIdentiferI\nfunc NewEntityIdentifier(v EntityIdentifierI) EntityIdentifier {\n\treturn EntityIdentifier{EntityIdentifierI: v}\n}\n\n// UnmarshalJSON implements RawMessage\nfunc (p *EntityIdentifier) UnmarshalJSON(data []byte) error {\n\tvar errors []error\n\n\tvar iri EntityIRI\n\terr := json.Unmarshal(data, &iri)\n\tif err == nil {\n\t\tp.EntityIdentifierI = iri\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tvar bnode EntityBNode\n\terr = json.Unmarshal(data, &bnode)\n\tif err == nil {\n\t\tp.EntityIdentifierI = bnode\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tvar s EntityIdentifierString\n\terr = json.Unmarshal(data, &s)\n\tif err == nil {\n\t\tp.EntityIdentifierI = s\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\treturn formatMultiError(errors)\n}\n\n// EntityIRI is an entity IRI.\ntype EntityIRI quad.IRI\n\n// BuildIdentifier implements EntityIdentifier\nfunc (iri EntityIRI) BuildIdentifier(ns *voc.Namespaces) (quad.Value, error) {\n\treturn quad.IRI(iri).FullWith(ns), nil\n}\n\n// EntityBNode is an entity BNode.\ntype EntityBNode quad.BNode\n\n// BuildIdentifier implements EntityIdentifier\nfunc (i EntityBNode) BuildIdentifier(ns *voc.Namespaces) (quad.Value, error) {\n\treturn quad.BNode(i), nil\n}\n\n// EntityIdentifierString is an entity IRI or BNode strings.\ntype EntityIdentifierString string\n\n// BuildIdentifier implements EntityIdentifier\nfunc (i EntityIdentifierString) BuildIdentifier(ns *voc.Namespaces) (quad.Value, error) {\n\tidentifier, err := parseIdentifier(string(i))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn AbsoluteValue(identifier, ns), nil\n}\n"
  },
  {
    "path": "query/linkedql/errors.go",
    "content": "package linkedql\n\nimport \"fmt\"\n\nfunc formatMultiError(errors []error) error {\n\tjoinedErr := \"\"\n\tfor _, err := range errors {\n\t\tjoinedErr += \"; \" + err.Error()\n\t}\n\treturn fmt.Errorf(\"Could not parse PropertyPath: %v\", joinedErr)\n}\n"
  },
  {
    "path": "query/linkedql/graph_pattern.go",
    "content": "package linkedql\n\n// GraphPattern represents a JSON-LD document\ntype GraphPattern = map[string]interface{}\n"
  },
  {
    "path": "query/linkedql/iter_docs.go",
    "content": "package linkedql\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/piprate/json-gold/ld\"\n)\n\nvar (\n\t_ query.Iterator = (*DocumentIterator)(nil)\n)\n\n// DocumentIterator is an iterator of documents from the graph\ntype DocumentIterator struct {\n\ttagsIt    *TagsIterator\n\tdataset   *ld.RDFDataset\n\terr       error\n\texhausted bool\n}\n\n// NewDocumentIterator returns a new DocumentIterator for a QuadStore and Path.\nfunc NewDocumentIterator(valueIt *ValueIterator) *DocumentIterator {\n\ttagsIt := &TagsIterator{ValueIt: valueIt, Selected: nil}\n\treturn &DocumentIterator{tagsIt: tagsIt, exhausted: false}\n}\n\nfunc (it *DocumentIterator) getDataset(ctx context.Context) (*ld.RDFDataset, error) {\n\td := ld.NewRDFDataset()\n\tfor it.tagsIt.Next(ctx) {\n\t\tr := it.tagsIt.ValueIt.scanner.Result()\n\t\tif err := it.tagsIt.Err(); err != nil {\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif r == nil {\n\t\t\tcontinue\n\t\t}\n\t\terr := it.tagsIt.addResultsToDataset(d, r)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn d, nil\n}\n\n// Next implements query.Iterator.\nfunc (it *DocumentIterator) Next(ctx context.Context) bool {\n\tif !it.exhausted {\n\t\td, err := it.getDataset(ctx)\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t} else {\n\t\t\tit.dataset = d\n\t\t}\n\t\tit.exhausted = true\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Result implements query.Iterator.\nfunc (it *DocumentIterator) Result() interface{} {\n\tcontext := make(map[string]interface{})\n\topts := ld.NewJsonLdOptions(\"\")\n\tc, err := datasetToCompact(it.dataset, context, opts)\n\tif err != nil {\n\t\tit.err = err\n\t}\n\treturn c\n}\n\n// Err implements query.Iterator.\nfunc (it *DocumentIterator) Err() error {\n\tif it.tagsIt == nil {\n\t\treturn nil\n\t}\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\treturn it.tagsIt.Err()\n}\n\n// Close implements query.Iterator.\nfunc (it *DocumentIterator) Close() error {\n\tif it.tagsIt == nil {\n\t\treturn nil\n\t}\n\treturn it.tagsIt.Close()\n}\n"
  },
  {
    "path": "query/linkedql/iter_tags.go",
    "content": "package linkedql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/piprate/json-gold/ld\"\n)\n\nvar _ query.Iterator = (*TagsIterator)(nil)\n\n// TagsIterator is a result iterator for records consisting of selected tags\n// or all the tags in the query.\ntype TagsIterator struct {\n\tValueIt   *ValueIterator\n\tSelected  []string\n\tExcludeID bool\n\terr       error\n}\n\n// NewTagsIterator creates a new TagsIterator\nfunc NewTagsIterator(valueIt *ValueIterator, selected []string, excludeID bool) TagsIterator {\n\treturn TagsIterator{\n\t\tValueIt:   valueIt,\n\t\tSelected:  selected,\n\t\tExcludeID: excludeID,\n\t\terr:       nil,\n\t}\n}\n\n// Next implements query.Iterator.\nfunc (it *TagsIterator) Next(ctx context.Context) bool {\n\treturn it.ValueIt.Next(ctx)\n}\n\nfunc (it *TagsIterator) addQuadFromRef(dataset *ld.RDFDataset, subject ld.Node, tag string, ref refs.Ref) error {\n\tp := ld.NewIRI(tag)\n\trname, err := it.ValueIt.Namer.NameOf(ref)\n\tif err != nil {\n\t\treturn err\n\t}\n\to, err := jsonld.ToNode(rname)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq := ld.NewQuad(subject, p, o, \"\")\n\tdataset.Graphs[\"@default\"] = append(dataset.Graphs[\"@default\"], q)\n\treturn nil\n}\n\nfunc toSubject(namer refs.Namer, result refs.Ref) (ld.Node, error) {\n\tv, err := namer.NameOf(result)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tid, ok := v.(quad.Identifier)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Expected subject to be an entity identifier but instead received: %v\", v)\n\t}\n\treturn jsonld.ToNode(id)\n}\n\nfunc (it *TagsIterator) addResultsToDataset(dataset *ld.RDFDataset, result refs.Ref) error {\n\ts, err := toSubject(it.ValueIt.Namer, result)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trefTags := make(map[string]refs.Ref)\n\n\tit.ValueIt.scanner.TagResults(refTags)\n\n\tif len(it.Selected) == 0 {\n\t\tfor tag, ref := range refTags {\n\t\t\tit.addQuadFromRef(dataset, s, tag, ref)\n\t\t}\n\t} else {\n\t\tfor _, tag := range it.Selected {\n\t\t\tit.addQuadFromRef(dataset, s, tag, refTags[tag])\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Result implements query.Iterator.\nfunc (it *TagsIterator) Result() interface{} {\n\t// FIXME(iddan): only convert when collation is JSON/JSON-LD, leave as Ref otherwise\n\tr := it.ValueIt.scanner.Result()\n\tif r == nil {\n\t\treturn nil\n\t}\n\td := ld.NewRDFDataset()\n\terr := it.addResultsToDataset(d, r)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn nil\n\t}\n\tdoc, err := singleDocumentFromRDF(d)\n\tif err != nil {\n\t\tit.err = err\n\t\treturn nil\n\t}\n\tif !it.ExcludeID {\n\t\tm := doc.(map[string]interface{})\n\t\tdelete(m, \"@id\")\n\t\treturn m\n\t}\n\treturn doc\n}\n\n// Err implements query.Iterator.\nfunc (it *TagsIterator) Err() error {\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\treturn it.ValueIt.Err()\n}\n\n// Close implements query.Iterator.\nfunc (it *TagsIterator) Close() error {\n\treturn it.ValueIt.Close()\n}\n"
  },
  {
    "path": "query/linkedql/iter_tags_test.go",
    "content": "package linkedql\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/piprate/json-gold/ld\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar (\n\tnamespace       = \"http://example.com/\"\n\talice           = namespace + \"alice\"\n\tlikes           = namespace + \"likes\"\n\tblank           = quad.RandomBlankNode()\n\tname            = namespace + \"name\"\n\taliceName       = quad.String(\"Alice\")\n\taliceLikesBlank = quad.Quad{\n\t\tSubject:   quad.IRI(alice),\n\t\tPredicate: quad.IRI(likes),\n\t\tObject:    blank,\n\t}\n\taliceNameAlice = quad.Quad{\n\t\tSubject:   quad.IRI(alice),\n\t\tPredicate: quad.IRI(name),\n\t\tObject:    aliceName,\n\t}\n)\n\nvar testCases = []struct {\n\tname     string\n\tdata     quad.Quad\n\tvalue    quad.Value\n\texpected ld.Node\n\terr      error\n}{\n\t{\n\t\tname:     \"Success for IRI\",\n\t\tdata:     aliceLikesBlank,\n\t\tvalue:    aliceLikesBlank.Subject,\n\t\texpected: ld.NewIRI(alice),\n\t\terr:      nil,\n\t},\n\t{\n\t\tname:     \"Success for Blank Node\",\n\t\tdata:     aliceLikesBlank,\n\t\tvalue:    aliceLikesBlank.Object,\n\t\texpected: ld.NewBlankNode(string(blank)),\n\t\terr:      nil,\n\t},\n\t{\n\t\tname:     \"Failure for String\",\n\t\tdata:     aliceNameAlice,\n\t\tvalue:    aliceNameAlice.Object,\n\t\texpected: nil,\n\t\terr:      fmt.Errorf(\"Expected subject to be an entity identifier but instead received: %v\", aliceName),\n\t},\n}\n\nfunc TestToSubject(t *testing.T) {\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\tstore := memstore.New(testCase.data)\n\t\t\tr, err := store.ValueOf(testCase.value)\n\t\t\trequire.NoError(t, err)\n\t\t\ts, err := toSubject(store, r)\n\t\t\tif testCase.err == nil {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, testCase.expected, s)\n\t\t\t} else {\n\t\t\t\trequire.Equal(t, testCase.err, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/linkedql/iter_values.go",
    "content": "package linkedql\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nvar _ query.Iterator = (*ValueIterator)(nil)\n\n// ValueIterator is an iterator of values from the graph.\ntype ValueIterator struct {\n\tNamer   refs.Namer\n\tpath    *path.Path\n\tscanner iterator.Scanner\n\terr     error\n}\n\n// NewValueIterator returns a new ValueIterator for a path and namer.\nfunc NewValueIterator(p *path.Path, namer refs.Namer) *ValueIterator {\n\treturn &ValueIterator{Namer: namer, path: p}\n}\n\n// NewValueIteratorFromPathStep attempts to build a path from PathStep and return a new ValueIterator of it.\n// If BuildPath fails returns error.\nfunc NewValueIteratorFromPathStep(step PathStep, qs graph.QuadStore, ns *voc.Namespaces) (*ValueIterator, error) {\n\tp, err := step.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewValueIterator(p, qs), nil\n}\n\n// Next implements query.Iterator.\nfunc (it *ValueIterator) Next(ctx context.Context) bool {\n\tif it.scanner == nil {\n\t\tit.scanner = it.path.BuildIterator(ctx).Iterate()\n\t}\n\treturn it.scanner.Next(ctx)\n}\n\n// Value returns the current value\nfunc (it *ValueIterator) Value() quad.Value {\n\tif it.scanner == nil {\n\t\treturn nil\n\t}\n\trname, err := it.Namer.NameOf(it.scanner.Result())\n\tif err != nil {\n\t\tit.err = err\n\t}\n\treturn rname\n}\n\n// Result implements query.Iterator.\nfunc (it *ValueIterator) Result() interface{} {\n\t// FIXME(iddan): only convert when collation is JSON/JSON-LD, leave as Ref otherwise\n\treturn jsonld.FromValue(it.Value())\n}\n\n// Err implements query.Iterator.\nfunc (it *ValueIterator) Err() error {\n\tif it.err != nil {\n\t\treturn it.err\n\t}\n\tif it.scanner == nil {\n\t\treturn nil\n\t}\n\treturn it.scanner.Err()\n}\n\n// Close implements query.Iterator.\nfunc (it *ValueIterator) Close() error {\n\tif it.scanner == nil {\n\t\treturn nil\n\t}\n\treturn it.scanner.Close()\n}\n"
  },
  {
    "path": "query/linkedql/jsonld_util.go",
    "content": "package linkedql\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/piprate/json-gold/ld\"\n)\n\n// datasetToCompact transforms a RDF dataset to a compact JSON-LD document.\nfunc datasetToCompact(dataset *ld.RDFDataset, context interface{}, opts *ld.JsonLdOptions) (interface{}, error) {\n\tapi := ld.NewJsonLdApi()\n\tproc := ld.NewJsonLdProcessor()\n\td, err := api.FromRDF(dataset, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc, err := proc.Compact(d, context, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn c, nil\n}\n\n// singleDocumentFromRDF transforms a RDF dataset to a single map JSON-LD document.\nfunc singleDocumentFromRDF(dataset *ld.RDFDataset) (interface{}, error) {\n\tapi := ld.NewJsonLdApi()\n\topts := ld.NewJsonLdOptions(\"\")\n\tdocuments, err := api.FromRDF(dataset, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(documents) != 1 {\n\t\treturn nil, fmt.Errorf(\"Unexpected number of documents\")\n\t}\n\treturn documents[0], nil\n}\n"
  },
  {
    "path": "query/linkedql/linkedql.go",
    "content": "package linkedql\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nconst (\n\t// Name is the name exposed to the query interface.\n\tName = \"linkedql\"\n\t// Namespace is an RDF namespace used for LinkedQL classes.\n\tNamespace = \"http://cayley.io/linkedql#\"\n\t// Prefix is an RDF namespace prefix used for LinkedQL classes.\n\tPrefix = \"linkedql:\"\n)\n\nfunc init() {\n\t// IRI namespace support\n\tvoc.Register(voc.Namespace{Full: Namespace, Prefix: Prefix})\n\t// register the language\n\tquery.RegisterLanguage(query.Language{\n\t\tName: Name,\n\t\tSession: func(qs graph.QuadStore) query.Session {\n\t\t\treturn NewSession(qs)\n\t\t},\n\t})\n}\n\nvar _ query.Session = &Session{}\n\n// Session represents a LinkedQL query processing.\ntype Session struct {\n\tqs graph.QuadStore\n}\n\n// NewSession creates a new Session.\nfunc NewSession(qs graph.QuadStore) *Session {\n\treturn &Session{\n\t\tqs: qs,\n\t}\n}\n\n// Execute for a given context, query and options return an iterator of results.\nfunc (s *Session) Execute(ctx context.Context, query string, opt query.Options) (query.Iterator, error) {\n\titem, err := Unmarshal([]byte(query))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tns := voc.Namespaces{}\n\tstep, ok := item.(Step)\n\tif !ok {\n\t\treturn nil, errors.New(\"must execute a Step\")\n\t}\n\treturn BuildIterator(step, s.qs, &ns)\n}\n\n// BuildIterator for given Step returns a query.Iterator\nfunc BuildIterator(step Step, qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error) {\n\tswitch s := step.(type) {\n\tcase IteratorStep:\n\t\treturn s.BuildIterator(qs, ns)\n\tcase PathStep:\n\t\treturn NewValueIteratorFromPathStep(s, qs, ns)\n\t}\n\treturn nil, errors.New(\"must execute a IteratorStep or PathStep\")\n}\n"
  },
  {
    "path": "query/linkedql/property_path.go",
    "content": "package linkedql\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\n// PropertyPathI is an interface to be used where a path of properties is expected.\ntype PropertyPathI interface {\n\tBuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error)\n}\n\n// PropertyPath is a struct wrapping PropertyPathI\ntype PropertyPath struct {\n\tPropertyPathI\n}\n\n// NewPropertyPath constructs a new PropertyPath\nfunc NewPropertyPath(p PropertyPathI) *PropertyPath {\n\treturn &PropertyPath{PropertyPathI: p}\n}\n\n// Description implements Step.\nfunc (*PropertyPath) Description() string {\n\treturn \"PropertyPath is a string, multiple strins or path describing a set of properties\"\n}\n\n// UnmarshalJSON implements RawMessage\nfunc (p *PropertyPath) UnmarshalJSON(data []byte) error {\n\tvar errors []error\n\n\tvar propertyIRIs PropertyIRIs\n\terr := json.Unmarshal(data, &propertyIRIs)\n\tif err == nil {\n\t\tp.PropertyPathI = propertyIRIs\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tvar propertyIRIStrings PropertyIRIStrings\n\terr = json.Unmarshal(data, &propertyIRIStrings)\n\tif err == nil {\n\t\tp.PropertyPathI = propertyIRIStrings\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tvar propertyIRI PropertyIRI\n\terr = json.Unmarshal(data, &propertyIRI)\n\tif err == nil {\n\t\tp.PropertyPathI = propertyIRI\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tvar propertyIRIString PropertyIRIString\n\terr = json.Unmarshal(data, &propertyIRIString)\n\tif err == nil {\n\t\tp.PropertyPathI = propertyIRIString\n\t\treturn nil\n\t}\n\terrors = append(errors, err)\n\n\tstep, err := Unmarshal(data)\n\tif err == nil {\n\t\tpathStep, ok := step.(PathStep)\n\t\tif ok {\n\t\t\tp.PropertyPathI = pathStep\n\t\t\treturn nil\n\t\t}\n\t\terrors = append(errors, fmt.Errorf(\"Step of type %T is not a PathStep. A PropertyPath step must be a PathStep\", step))\n\t}\n\terrors = append(errors, err)\n\n\treturn formatMultiError(errors)\n}\n\n// PropertyIRIs is a slice of property IRIs.\ntype PropertyIRIs []PropertyIRI\n\n// BuildPath implements PropertyPath.\nfunc (p PropertyIRIs) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tvar values []quad.Value\n\tfor _, iri := range p {\n\t\tvalues = append(values, iri.full(ns))\n\t}\n\treturn path.StartPath(qs, values...), nil\n}\n\n// PropertyIRIStrings is a slice of property IRI strings.\ntype PropertyIRIStrings []string\n\n// PropertyIRIs casts PropertyIRIStrings into PropertyIRIs\nfunc (p PropertyIRIStrings) PropertyIRIs() PropertyIRIs {\n\tvar iris PropertyIRIs\n\tfor _, iri := range p {\n\t\tiris = append(iris, PropertyIRI(iri))\n\t}\n\treturn iris\n}\n\n// BuildPath implements PropertyPath.\nfunc (p PropertyIRIStrings) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\treturn p.PropertyIRIs().BuildPath(qs, ns)\n}\n\n// PropertyIRI is an IRI of a Property\ntype PropertyIRI quad.IRI\n\nfunc (p PropertyIRI) full(ns *voc.Namespaces) quad.IRI {\n\treturn quad.IRI(p).FullWith(ns)\n}\n\n// BuildPath implements PropertyPath\nfunc (p PropertyIRI) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\treturn path.StartPath(qs, p.full(ns)), nil\n}\n\n// PropertyIRIString is a string of IRI of a Property\ntype PropertyIRIString string\n\n// BuildPath implements PropertyPath\nfunc (p PropertyIRIString) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tiri := PropertyIRI(p)\n\treturn iri.BuildPath(qs, ns)\n}\n\n// PropertyStep is a step that should resolve to a path of properties\ntype PropertyStep struct {\n\tPathStep\n}\n\n// BuildPath implements PropertyPath\nfunc (p PropertyStep) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\treturn p.BuildPath(qs, ns)\n}\n"
  },
  {
    "path": "query/linkedql/registry.go",
    "content": "package linkedql\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/piprate/json-gold/ld\"\n)\n\nvar (\n\ttypeByName = make(map[string]reflect.Type)\n\tnameByType = make(map[reflect.Type]string)\n)\n\n// TypeByName returns a type by its registration name. See Register.\nfunc TypeByName(name string) (reflect.Type, bool) {\n\tt, ok := typeByName[name]\n\treturn t, ok\n}\n\n// RegisteredTypes returns type names of all registered types.\nfunc RegisteredTypes() []string {\n\tout := make([]string, 0, len(typeByName))\n\tfor k := range typeByName {\n\t\tout = append(out, k)\n\t}\n\treturn out\n}\n\n// RegistryItem in the registry.\ntype RegistryItem interface {\n\tDescription() string\n}\n\n// Register adds an Item type to the registry.\nfunc Register(typ RegistryItem) {\n\ttp := reflect.TypeOf(typ)\n\tif tp.Kind() == reflect.Ptr {\n\t\ttp = tp.Elem()\n\t}\n\tif tp.Kind() != reflect.Struct {\n\t\tpanic(\"only structs are allowed\")\n\t}\n\tname := Namespace + tp.Name()\n\tif _, ok := typeByName[name]; ok {\n\t\tpanic(\"this name was already registered\")\n\t}\n\ttypeByName[name] = tp\n\tnameByType[tp] = name\n}\n\nvar (\n\tgraphPattern   = reflect.TypeOf(GraphPattern(nil))\n\tquadValue      = reflect.TypeOf((*quad.Value)(nil)).Elem()\n\tquadSliceValue = reflect.TypeOf([]quad.Value{})\n\tquadIRI        = reflect.TypeOf(quad.IRI(\"\"))\n\tquadSliceIRI   = reflect.TypeOf([]quad.IRI{})\n)\n\n// Unmarshal attempts to unmarshal an Item or returns error.\nfunc Unmarshal(data []byte) (RegistryItem, error) {\n\t// TODO: make it a part of quad/jsonld package.\n\tdata, err := normalizeQuery(data)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar m map[string]json.RawMessage\n\tif err := json.Unmarshal(data, &m); err != nil {\n\t\treturn nil, err\n\t}\n\tvar typ string\n\tif err := json.Unmarshal(m[\"@type\"], &typ); err != nil {\n\t\treturn nil, err\n\t}\n\tdelete(m, \"@type\")\n\ttp, ok := TypeByName(typ)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unsupported item: %q\", typ)\n\t}\n\titem := reflect.New(tp).Elem()\n\tfor i := 0; i < tp.NumField(); i++ {\n\t\tf := tp.Field(i)\n\t\tname := f.Name\n\t\ttag := strings.SplitN(f.Tag.Get(\"json\"), \",\", 2)[0]\n\t\tif tag == \"-\" {\n\t\t\tcontinue\n\t\t} else if tag != \"\" {\n\t\t\tname = Namespace + tag\n\t\t}\n\t\tv, ok := m[name]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfv := item.Field(i)\n\t\tswitch f.Type {\n\t\tcase graphPattern:\n\t\t\tvar a interface{}\n\t\t\terr := json.Unmarshal(v, &a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(a))\n\t\t\tcontinue\n\t\tcase quadValue:\n\t\t\tvar a interface{}\n\t\t\terr := json.Unmarshal(v, &a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tvalue, err := parseValue(a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(value))\n\t\t\tcontinue\n\t\tcase quadSliceValue:\n\t\t\tvar a interface{}\n\t\t\terr := json.Unmarshal(v, &a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tarr, ok := a.([]interface{})\n\t\t\tif !ok {\n\t\t\t\tarr = []interface{}{a}\n\t\t\t}\n\t\t\tvar values []quad.Value\n\t\t\tfor _, item := range arr {\n\t\t\t\tvalue, err := parseValue(item)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tvalues = append(values, value)\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(values))\n\t\t\tcontinue\n\t\tcase quadIRI:\n\t\t\tvar a interface{}\n\t\t\terr := json.Unmarshal(v, &a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ts, ok := a.(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"Expected a string but received %v instead\", a)\n\t\t\t}\n\t\t\tval, err := parseIRI(s)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(val))\n\t\t\tcontinue\n\t\tcase quadSliceIRI:\n\t\t\tvar a interface{}\n\t\t\terr := json.Unmarshal(v, &a)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tarr, ok := a.([]interface{})\n\t\t\tif !ok {\n\t\t\t\tarr = []interface{}{a}\n\t\t\t}\n\t\t\tvar values []quad.IRI\n\t\t\tfor _, item := range arr {\n\t\t\t\ts, ok := item.(string)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"Expected a string but received %v instead\", item)\n\t\t\t\t}\n\t\t\t\tval, err := parseIRI(s)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tvalues = append(values, val)\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(values))\n\t\t\tcontinue\n\t\t}\n\t\tswitch f.Type.Kind() {\n\t\tcase reflect.Interface:\n\t\t\ts, err := Unmarshal(v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfv.Set(reflect.ValueOf(s))\n\t\tcase reflect.Slice:\n\t\t\tel := f.Type.Elem()\n\t\t\tif el.Kind() != reflect.Interface {\n\t\t\t\terr := json.Unmarshal(v, fv.Addr().Interface())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar arr []json.RawMessage\n\t\t\t\terr := json.Unmarshal(v, &arr)\n\t\t\t\tif err != nil {\n\t\t\t\t\tvar i json.RawMessage\n\t\t\t\t\tiErr := json.Unmarshal(v, &i)\n\t\t\t\t\tif iErr != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tarr = []json.RawMessage{i}\n\t\t\t\t}\n\t\t\t\tif arr != nil {\n\t\t\t\t\tva := reflect.MakeSlice(f.Type, len(arr), len(arr))\n\t\t\t\t\tfor i, v := range arr {\n\t\t\t\t\t\ts, err := Unmarshal(v)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tva.Index(i).Set(reflect.ValueOf(s))\n\t\t\t\t\t}\n\t\t\t\t\tfv.Set(va)\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\terr := json.Unmarshal(v, fv.Addr().Interface())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\treturn item.Addr().Interface().(RegistryItem), nil\n}\n\nfunc normalizeQuery(data []byte) ([]byte, error) {\n\tvar query interface{}\n\tjson.Unmarshal(data, &query)\n\tprocessor := ld.NewJsonLdProcessor()\n\topts := ld.NewJsonLdOptions(\"\")\n\tcompact, err := processor.Compact(query, nil, opts)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn json.Marshal(compact)\n}\n\nfunc parseBNode(s string) (quad.BNode, error) {\n\tif !strings.HasPrefix(s, \"_:\") {\n\t\treturn \"\", fmt.Errorf(\"blank node ID must start with \\\"_:\\\"\")\n\t}\n\treturn quad.BNode(s[2:]), nil\n}\n\nfunc parseIRI(s string) (quad.IRI, error) {\n\treturn quad.IRI(s), nil\n}\n\nfunc parseIdentifier(s string) (quad.Value, error) {\n\tbnode, err := parseBNode(s)\n\tif err == nil {\n\t\treturn bnode, nil\n\t}\n\tiri, err := parseIRI(s)\n\tif err == nil {\n\t\treturn iri, nil\n\t}\n\treturn nil, fmt.Errorf(\"can not parse JSON-LD identifier: %#v\", s)\n}\n\nfunc parseIdentifierString(a interface{}) (string, error) {\n\tm, ok := a.(map[string]interface{})\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"unexpected type: %T\", a)\n\t}\n\tid, ok := m[\"@id\"].(string)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"expected a @id key\")\n\t}\n\treturn id, nil\n}\n\nfunc parseLiteral(a interface{}) (quad.Value, error) {\n\tswitch a := a.(type) {\n\tcase string:\n\t\treturn quad.String(a), nil\n\tcase int64:\n\t\treturn quad.Int(a), nil\n\tcase float64:\n\t\ti := int64(a)\n\t\tif a == float64(i) {\n\t\t\treturn quad.Int(i), nil\n\t\t}\n\t\treturn quad.Float(a), nil\n\tcase bool:\n\t\treturn quad.Bool(a), nil\n\tcase map[string]interface{}:\n\t\tif val, ok := a[\"@value\"].(string); ok {\n\t\t\tif lang, ok := a[\"@language\"].(string); ok {\n\t\t\t\treturn quad.LangString{Value: quad.String(val), Lang: lang}, nil\n\t\t\t}\n\t\t\tif typ, ok := a[\"@type\"].(string); ok {\n\t\t\t\treturn quad.TypedString{Value: quad.String(val), Type: quad.IRI(typ)}, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"can not parse %#v as a literal\", a)\n}\n\nfunc parseValue(a interface{}) (quad.Value, error) {\n\tidentifierString, err := parseIdentifierString(a)\n\tif err == nil {\n\t\tidentifier, err := parseIdentifier(identifierString)\n\t\tif err == nil {\n\t\t\treturn identifier, nil\n\t\t}\n\t}\n\tlit, err := parseLiteral(a)\n\tif err == nil {\n\t\treturn lit, nil\n\t}\n\treturn nil, fmt.Errorf(\"can not parse JSON-LD value: %#v\", a)\n}\n"
  },
  {
    "path": "query/linkedql/registry_test.go",
    "content": "package linkedql\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc init() {\n\tRegister(&TestStep{})\n}\n\nvar unmarshalCases = []struct {\n\tname string\n\tdata string\n\texp  Step\n}{\n\t{\n\t\tname: \"simple\",\n\t\tdata: `{\n\t\"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n\t\"@type\": \"TestStep\",\n\t\"limit\": 10\n}`,\n\t\texp: &TestStep{Limit: 10},\n\t},\n\t{\n\t\tname: \"simple\",\n\t\tdata: `{\n\t\"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n\t\"@type\": \"TestStep\",\n\t\"tags\": [\"a\", \"b\"]\n}`,\n\t\texp: &TestStep{Tags: []string{\"a\", \"b\"}},\n\t},\n\t{\n\t\tname: \"nested\",\n\t\tdata: `{\n\t\"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n\t\"@type\": \"TestStep\",\n\t\"limit\": 10,\n\t\"from\": {\n\t\t\"@type\": \"TestStep\",\n\t\t\"limit\": 15,\n\t\t\"from\": {\n\t\t\t\"@type\": \"TestStep\",\n\t\t\t\"limit\": 20\n\t\t}\n\t}\n}`,\n\t\texp: &TestStep{\n\t\t\tLimit: 10,\n\t\t\tFrom: &TestStep{\n\t\t\t\tLimit: 15,\n\t\t\t\tFrom: &TestStep{\n\t\t\t\t\tLimit: 20,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\tname: \"nested slice\",\n\t\tdata: `{\n\t\"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n\t\"@type\": \"TestStep\",\n\t\"limit\": 10,\n\t\"sub\": [\n\t\t{\n\t\t\t\"@type\": \"TestStep\",\n\t\t\t\"limit\": 15\n\t\t},\n\t\t{\n\t\t\t\"@type\": \"TestStep\",\n\t\t\t\"limit\": 20\n\t\t}\n\t]\n}`,\n\t\texp: &TestStep{\n\t\t\tLimit: 10,\n\t\t\tSub: []PathStep{\n\t\t\t\t&TestStep{\n\t\t\t\t\tLimit: 15,\n\t\t\t\t},\n\t\t\t\t&TestStep{\n\t\t\t\t\tLimit: 20,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n}\n\ntype TestStep struct {\n\tLimit int        `json:\"limit\"`\n\tTags  []string   `json:\"tags\"`\n\tFrom  PathStep   `json:\"from\"`\n\tSub   []PathStep `json:\"sub\"`\n}\n\nfunc (s *TestStep) Description() string {\n\treturn \"A TestStep for checking the registry\"\n}\n\nfunc (s *TestStep) BuildIterator(qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error) {\n\tpanic(\"Can't build iterator for TestStep\")\n}\n\nfunc (s *TestStep) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tpanic(\"Can't build path for TestStep\")\n}\n\nfunc TestUnmarshalStep(t *testing.T) {\n\tfor _, c := range unmarshalCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\ts, err := Unmarshal([]byte(c.data))\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, c.exp, s)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/linkedql/step_types.go",
    "content": "package linkedql\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\n// Step is a logical part in the query\ntype Step interface {\n\tRegistryItem\n}\n\n// IteratorStep is a step that can build an Iterator.\ntype IteratorStep interface {\n\tStep\n\tBuildIterator(qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error)\n}\n\n// PathStep is a Step that can build a Path.\ntype PathStep interface {\n\tStep\n\tBuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error)\n}\n"
  },
  {
    "path": "query/linkedql/steps/as.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&As{})\n}\n\nvar _ linkedql.PathStep = (*As)(nil)\n\n// As corresponds to .tag().\ntype As struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tName string            `json:\"name\"`\n}\n\n// Description implements Step.\nfunc (s *As) Description() string {\n\treturn \"assigns the resolved values of the from step to a given name. The name can be used with the Select and Documents steps to retrieve the values or to return to the values in further steps with the Back step. It resolves to the values of the from step.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *As) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Tag(s.Name), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/back.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Back{})\n}\n\nvar _ linkedql.PathStep = (*Back)(nil)\n\n// Back corresponds to .back().\ntype Back struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tName string            `json:\"name\"`\n}\n\n// Description implements Step.\nfunc (s *Back) Description() string {\n\treturn \"resolves to the values of the previous the step or the values assigned to name in a former step.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Back) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Back(s.Name), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/both.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Both{})\n}\n\nvar _ linkedql.PathStep = (*Both)(nil)\n\n// Both corresponds to .both().\ntype Both struct {\n\tFrom       linkedql.PathStep      `json:\"from\"`\n\tProperties *linkedql.PropertyPath `json:\"properties\"`\n}\n\n// Description implements Step.\nfunc (s *Both) Description() string {\n\treturn \"is like View but resolves to both the object values and references to the values of the given properties in via. It is the equivalent for the Union of View and ViewReverse of the same property.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Both) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tviaPath, err := s.Properties.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Both(viaPath), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/collect.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Collect{})\n}\n\nvar _ linkedql.PathStep = (*Collect)(nil)\n\n// Collect corresponds to .view().\ntype Collect struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tName quad.IRI          `json:\"name\"`\n}\n\n// Description implements Step.\nfunc (s *Collect) Description() string {\n\treturn \"Recursively resolves values of a list (also known as RDF collection)\"\n}\n\nvar (\n\tfirst  = quad.IRI(\"rdf:first\").Full()\n\trest   = quad.IRI(\"rdf:rest\").Full()\n\trdfNil = quad.IRI(\"rdf:nil\").Full()\n)\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Collect) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := fromPath.\n\t\tOut(s.Name).\n\t\tSave(first, string(first)).\n\t\tSave(rest, string(rest)).\n\t\tOr(\n\t\t\tfromPath.Out(s.Name).FollowRecursive(rest, -1, nil).\n\t\t\t\tSave(first, string(first)).\n\t\t\t\tSave(rest, string(rest)),\n\t\t).\n\t\tOr(fromPath.Save(s.Name, string(s.Name)))\n\treturn p, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/count.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Count{})\n}\n\nvar _ linkedql.PathStep = (*Count)(nil)\n\n// Count corresponds to .count().\ntype Count struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *Count) Description() string {\n\treturn \"resolves to the number of the resolved values of the from step\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Count) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Count(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/difference.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Difference{})\n}\n\nvar _ linkedql.PathStep = (*Difference)(nil)\n\n// Difference corresponds to .difference().\ntype Difference struct {\n\tFrom  linkedql.PathStep   `json:\"from\"`\n\tSteps []linkedql.PathStep `json:\"steps\"`\n}\n\n// Description implements Step.\nfunc (s *Difference) Description() string {\n\treturn \"resolves to all the values resolved by the from step different then the values resolved by the provided steps. Caution: it might be slow to execute.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Difference) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpath := fromPath\n\tfor _, step := range s.Steps {\n\t\tp, err := step.BuildPath(qs, ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpath = path.Except(p)\n\t}\n\treturn path, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/greater_than.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&GreaterThan{})\n}\n\nvar _ linkedql.PathStep = (*GreaterThan)(nil)\n\n// GreaterThan corresponds to gt().\ntype GreaterThan struct {\n\tFrom  linkedql.PathStep `json:\"from\"`\n\tValue quad.Value        `json:\"value\"`\n}\n\n// Description implements Step.\nfunc (s *GreaterThan) Description() string {\n\treturn \"Greater than equals filters out values that are not greater than given value\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *GreaterThan) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Filter(iterator.CompareGT, linkedql.AbsoluteValue(s.Value, ns)), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/greater_than_equals.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&GreaterThanEquals{})\n}\n\nvar _ linkedql.PathStep = (*GreaterThanEquals)(nil)\n\n// GreaterThanEquals corresponds to gte().\ntype GreaterThanEquals struct {\n\tFrom  linkedql.PathStep `json:\"from\"`\n\tValue quad.Value        `json:\"value\"`\n}\n\n// Description implements Step.\nfunc (s *GreaterThanEquals) Description() string {\n\treturn \"Greater than equals filters out values that are not greater than or equal given value\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *GreaterThanEquals) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Filter(iterator.CompareGTE, linkedql.AbsoluteValue(s.Value, ns)), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/has.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Has{})\n}\n\nvar _ linkedql.PathStep = (*Has)(nil)\n\n// Has corresponds to .has().\ntype Has struct {\n\tFrom     linkedql.PathStep      `json:\"from\"`\n\tProperty *linkedql.PropertyPath `json:\"property\"`\n\tValues   []quad.Value           `json:\"values\"`\n}\n\n// Description implements Step.\nfunc (s *Has) Description() string {\n\treturn \"filters all paths which are, at this point, on the subject for the given predicate and object, but do not follow the path, merely filter the possible paths. Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Has) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tviaPath, err := s.Property.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Has(viaPath, linkedql.AbsoluteValues(s.Values, ns)...), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/has_reverse.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&HasReverse{})\n}\n\nvar _ linkedql.PathStep = (*HasReverse)(nil)\n\n// HasReverse corresponds to .hasR().\ntype HasReverse struct {\n\tFrom     linkedql.PathStep      `json:\"from\"`\n\tProperty *linkedql.PropertyPath `json:\"property\"`\n\tValues   []quad.Value           `json:\"values\"`\n}\n\n// Description implements Step.\nfunc (s *HasReverse) Description() string {\n\treturn \"is the same as Has, but sets constraint in reverse direction.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *HasReverse) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tviaPath, err := s.Property.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.HasReverse(viaPath, linkedql.AbsoluteValues(s.Values, ns)...), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/in.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n)\n\nfunc init() {\n\tlinkedql.Register(&In{})\n}\n\nvar _ linkedql.PathStep = (*In)(nil)\n\n// In is an alias for ViewReverse.\ntype In struct {\n\tVisitReverse\n}\n\n// Description implements Step.\nfunc (s *In) Description() string {\n\treturn \"aliases for ViewReverse\"\n}\n"
  },
  {
    "path": "query/linkedql/steps/intersect.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Intersect{})\n}\n\nvar _ linkedql.PathStep = (*Intersect)(nil)\n\n// Intersect represents .intersect() and .and().\ntype Intersect struct {\n\tFrom  linkedql.PathStep   `json:\"from\"`\n\tSteps []linkedql.PathStep `json:\"steps\"`\n}\n\n// Description implements Step.\nfunc (s *Intersect) Description() string {\n\treturn \"resolves to all the same values resolved by the from step and the provided steps.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Intersect) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := fromPath\n\tfor _, step := range s.Steps {\n\t\tstepPath, err := step.BuildPath(qs, ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp = p.And(stepPath)\n\t}\n\treturn p, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/jsonld_util.go",
    "content": "package steps\n\nimport (\n\t\"fmt\"\n)\n\ntype ldArray = []interface{}\ntype ldMap = map[string]interface{}\n\nfunc unwrapValue(i interface{}) interface{} {\n\tm, ok := i.(ldMap)\n\tif ok && len(m) == 1 {\n\t\tv, ok := m[\"@value\"]\n\t\tif ok {\n\t\t\treturn v\n\t\t}\n\t}\n\treturn i\n}\n\nfunc unwrapSingle(i interface{}) interface{} {\n\ta, ok := i.(ldArray)\n\tif ok && len(a) == 1 {\n\t\treturn a[0]\n\t}\n\treturn i\n}\n\n// isomorphic checks whether source and target JSON-LD structures are the same\n// semantically. This function is not complete and is maintained for testing\n// purposes. Hopefully in the future it can be proven sufficient for general\n// purpose use.\nfunc isomorphic(source interface{}, target interface{}) error {\n\tsource = unwrapValue(unwrapSingle(source))\n\ttarget = unwrapValue(unwrapSingle(target))\n\tswitch s := source.(type) {\n\tcase string:\n\t\tt, ok := target.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"Expected %v to be a string but instead received %T\", target, target)\n\t\t}\n\t\tif s != t {\n\t\t\treturn fmt.Errorf(\"Expected \\\"%v\\\" but instead received \\\"%v\\\"\", s, t)\n\t\t}\n\t\treturn nil\n\tcase ldArray:\n\t\tt, ok := target.(ldArray)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"Expected multiple values but instead received the single value: %#v\", target)\n\t\t}\n\t\tif len(s) != len(t) {\n\t\t\treturn fmt.Errorf(\"Expected %#v and %#v to have the same length\", s, t)\n\t\t}\n\titems:\n\t\tfor _, i := range s {\n\t\t\tfor _, tI := range t {\n\t\t\t\tif isomorphic(i, tI) == nil {\n\t\t\t\t\tcontinue items\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"No matching values for the item %#v in %#v\", i, t)\n\t\t}\n\t\treturn nil\n\tcase ldMap:\n\t\tt, ok := target.(ldMap)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"Expected %#v to be a map or a slice with a single map but instead received %T\", target, target)\n\t\t}\n\t\tfor k, v := range s {\n\t\t\ttV, _ := t[k]\n\t\t\terr := isomorphic(v, tV)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/jsonld_util_test.go",
    "content": "package steps\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar testCases = []struct {\n\tname     string\n\tsource   interface{}\n\ttarget   interface{}\n\texpected error\n}{\n\t{\n\t\tname:     \"Single matching IDs\",\n\t\tsource:   map[string]interface{}{\"@id\": \"a\"},\n\t\ttarget:   map[string]interface{}{\"@id\": \"a\"},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single non matching IDs\",\n\t\tsource:   map[string]interface{}{\"@id\": \"a\"},\n\t\ttarget:   map[string]interface{}{\"@id\": \"b\"},\n\t\texpected: fmt.Errorf(\"Expected \\\"a\\\" but instead received \\\"b\\\"\"),\n\t},\n\t{\n\t\tname:     \"Single matching properties\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single non matching properties\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": \"Bob\"},\n\t\texpected: fmt.Errorf(\"Expected \\\"Alice\\\" but instead received \\\"Bob\\\"\"),\n\t},\n\t{\n\t\tname:     \"Single matching property with multiple values ordered\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\", \"Bob\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\", \"Bob\"}},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single matching property with multiple values unordered\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\", \"Bob\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Bob\", \"Alice\"}},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single non matching property with multiple values\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\", \"Bob\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Dan\", \"Alice\"}},\n\t\texpected: fmt.Errorf(\"No matching values for the item \\\"Bob\\\" in []interface {}{\\\"Dan\\\", \\\"Alice\\\"}\"),\n\t},\n\t{\n\t\tname:     \"Single non matching property with multiple values non matching length\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\", \"Bob\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": []interface{}{\"Alice\"}},\n\t\texpected: fmt.Errorf(\"Expected multiple values but instead received the single value: \\\"Alice\\\"\"),\n\t},\n\t{\n\t\tname: \"Single matching nested\",\n\t\tsource: map[string]interface{}{\n\t\t\t\"http://example.com/friend\": map[string]interface{}{\n\t\t\t\t\"@id\": \"alice\",\n\t\t\t},\n\t\t},\n\t\ttarget: map[string]interface{}{\n\t\t\t\"http://example.com/friend\": map[string]interface{}{\n\t\t\t\t\"@id\": \"alice\",\n\t\t\t},\n\t\t},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname: \"Single non matching nested\",\n\t\tsource: map[string]interface{}{\n\t\t\t\"http://example.com/friend\": map[string]interface{}{\n\t\t\t\t\"@id\": \"alice\",\n\t\t\t},\n\t\t},\n\t\ttarget: map[string]interface{}{\n\t\t\t\"http://example.com/friend\": map[string]interface{}{\n\t\t\t\t\"@id\": \"bob\",\n\t\t\t},\n\t\t},\n\t\texpected: fmt.Errorf(\"Expected \\\"alice\\\" but instead received \\\"bob\\\"\"),\n\t},\n\t{\n\t\tname:     \"Single matching properties with @value string\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Alice\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Alice\"}},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single non matching properties with @value string\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Alice\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Bob\"}},\n\t\texpected: fmt.Errorf(\"Expected \\\"Alice\\\" but instead received \\\"Bob\\\"\"),\n\t},\n\t{\n\t\tname:     \"Single matching properties with @value string and string\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Alice\"}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single matching properties with string and @value string\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": map[string]interface{}{\"@value\": \"Alice\"}},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single matching properties with @value string array string\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": []interface{}{map[string]interface{}{\"@value\": \"Alice\"}}},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\texpected: nil,\n\t},\n\t{\n\t\tname:     \"Single matching properties with string and @value string array\",\n\t\tsource:   map[string]interface{}{\"http://example.com/name\": \"Alice\"},\n\t\ttarget:   map[string]interface{}{\"http://example.com/name\": []interface{}{map[string]interface{}{\"@value\": \"Alice\"}}},\n\t\texpected: nil,\n\t},\n}\n\nfunc TestIsomorphic(t *testing.T) {\n\tfor _, c := range testCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\trequire.Equal(t, c.expected, isomorphic(c.source, c.target))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/linkedql/steps/labels.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Labels{})\n}\n\nvar _ linkedql.PathStep = (*Labels)(nil)\n\n// Labels corresponds to .labels().\ntype Labels struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *Labels) Description() string {\n\treturn \"gets the list of inbound and outbound quad labels\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Labels) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Labels(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/less_than.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&LessThan{})\n}\n\nvar _ linkedql.PathStep = (*LessThan)(nil)\n\n// LessThan corresponds to lt().\ntype LessThan struct {\n\tFrom  linkedql.PathStep `json:\"from\"`\n\tValue quad.Value        `json:\"value\"`\n}\n\n// Description implements Step.\nfunc (s *LessThan) Description() string {\n\treturn \"Less than filters out values that are not less than given value\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *LessThan) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Filter(iterator.CompareLT, s.Value), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/less_than_equals.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&LessThanEquals{})\n}\n\nvar _ linkedql.PathStep = (*LessThanEquals)(nil)\n\n// LessThanEquals corresponds to lte().\ntype LessThanEquals struct {\n\tFrom  linkedql.PathStep `json:\"from\"`\n\tValue quad.Value        `json:\"value\"`\n}\n\n// Description implements Step.\nfunc (s *LessThanEquals) Description() string {\n\treturn \"Less than equals filters out values that are not less than or equal given value\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *LessThanEquals) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Filter(iterator.CompareLTE, linkedql.AbsoluteValue(s.Value, ns)), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/like.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Like{})\n}\n\nvar _ linkedql.PathStep = (*Like)(nil)\n\n// Like corresponds to like().\ntype Like struct {\n\tFrom    linkedql.PathStep `json:\"from\"`\n\tPattern string            `json:\"likePattern\"`\n}\n\n// Description implements Operator.\nfunc (s *Like) Description() string {\n\treturn \"Like filters out values that do not match given pattern.\"\n}\n\n// BuildPath implements PathStep.\nfunc (s *Like) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Filters(shape.Wildcard{Pattern: s.Pattern}), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/limit.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Limit{})\n}\n\nvar _ linkedql.PathStep = (*Limit)(nil)\n\n// Limit corresponds to .limit().\ntype Limit struct {\n\tFrom  linkedql.PathStep `json:\"from\"`\n\tLimit int64             `json:\"limit\"`\n}\n\n// Description implements Step.\nfunc (s *Limit) Description() string {\n\treturn \"limits a number of nodes for current path.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Limit) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Limit(s.Limit), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/match.go",
    "content": "package steps\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/cayleygraph/quad/voc\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n\t\"github.com/cayleygraph/quad/voc/rdfs\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Match{})\n}\n\nvar _ linkedql.PathStep = (*Match)(nil)\n\n// Match corresponds to .has().\ntype Match struct {\n\tFrom    linkedql.PathStep     `json:\"from\" minCardinality:\"0\"`\n\tPattern linkedql.GraphPattern `json:\"pattern\"`\n}\n\n// Description implements Step.\nfunc (s *Match) Description() string {\n\treturn \"filters all paths which are, at this point, on the subject for the given predicate and object, but do not follow the path, merely filter the possible paths. Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Match) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tvar p *path.Path\n\tif s.From != nil {\n\t\tfromPath, err := s.From.BuildPath(qs, ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp = fromPath\n\t} else {\n\t\tp = path.StartPath(qs)\n\t}\n\n\t// Get quads\n\tquads, err := parsePattern(s.Pattern, ns)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.Follow(buildPatternPath(quads, ns)), nil\n}\n\ntype entityProperties = map[quad.Value]map[quad.Value][]quad.Value\n\nfunc entityPropertiesToPath(entity quad.Value, entities entityProperties) *path.Path {\n\tp := path.StartMorphism()\n\tif iri, ok := entity.(quad.IRI); ok {\n\t\tp = p.Is(iri)\n\t}\n\tfor property, values := range entities[entity] {\n\t\tfor _, value := range values {\n\t\t\tif _, ok := value.(quad.BNode); ok {\n\t\t\t\tp = p.Out(property).Follow(entityPropertiesToPath(value, entities)).Back(\"\")\n\t\t\t} else {\n\t\t\t\tp = p.Has(property, value)\n\t\t\t}\n\t\t}\n\t}\n\treturn p\n}\n\nfunc groupEntities(pattern []quad.Quad, ns *voc.Namespaces) (entityProperties, map[quad.Value]struct{}) {\n\t// referenced holds entities used as values for properties of other entities\n\treferenced := make(map[quad.Value]struct{})\n\n\tentities := make(entityProperties)\n\n\tfor _, q := range pattern {\n\t\tentity := linkedql.AbsoluteValue(q.Subject, ns)\n\t\tproperty := linkedql.AbsoluteValue(q.Predicate, ns)\n\t\tvalue := linkedql.AbsoluteValue(q.Object, ns)\n\n\t\tif _, ok := value.(quad.BNode); ok {\n\t\t\treferenced[value] = struct{}{}\n\t\t}\n\n\t\tproperties, ok := entities[entity]\n\t\tif !ok {\n\t\t\tproperties = make(map[quad.Value][]quad.Value)\n\t\t\tentities[entity] = properties\n\t\t}\n\t\tif isSingleEntityQuad(q) {\n\t\t\tcontinue\n\t\t}\n\t\tproperties[property] = append(properties[property], value)\n\t}\n\n\treturn entities, referenced\n}\n\n// buildPath for given pattern constructs a Path object\nfunc buildPatternPath(pattern []quad.Quad, ns *voc.Namespaces) *path.Path {\n\tentities, referenced := groupEntities(pattern, ns)\n\tp := path.StartMorphism()\n\n\tfor entity := range entities {\n\t\t// Apply only on entities which are not referenced as values\n\t\tif _, ok := referenced[entity]; !ok {\n\t\t\tp = p.Follow(entityPropertiesToPath(entity, entities))\n\t\t}\n\t}\n\n\treturn p\n}\n\nfunc contextualizePattern(pattern linkedql.GraphPattern, ns *voc.Namespaces) linkedql.GraphPattern {\n\tcontext := make(map[string]interface{})\n\tfor _, namespace := range ns.List() {\n\t\tcontext[namespace.Prefix] = namespace.Full\n\t}\n\tpatternClone := linkedql.GraphPattern{\n\t\t\"@context\": context,\n\t}\n\tfor key, value := range pattern {\n\t\tpatternClone[key] = value\n\t}\n\treturn pattern\n}\n\nfunc quadsFromMap(o interface{}) ([]quad.Quad, error) {\n\treader := jsonld.NewReaderFromMap(o)\n\treturn quad.ReadAll(reader)\n}\n\nfunc normalizeQuads(quads []quad.Quad, pattern linkedql.GraphPattern) ([]quad.Quad, error) {\n\tif id, ok := pattern[\"@id\"]; ok && len(quads) == 0 {\n\t\tidString, ok := id.(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"Unexpected type for @id %T\", idString)\n\t\t}\n\t\tquads = append(quads, makeSingleEntityQuad(quad.IRI(idString)))\n\t}\n\treturn quads, nil\n}\n\nfunc parsePattern(pattern linkedql.GraphPattern, ns *voc.Namespaces) ([]quad.Quad, error) {\n\tcontextualizedPattern := contextualizePattern(pattern, ns)\n\tquads, err := quadsFromMap(contextualizedPattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquads, err = normalizeQuads(quads, contextualizedPattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(quads) == 0 && len(pattern) != 0 {\n\t\treturn nil, fmt.Errorf(\"Pattern does not parse to any quad. `{}` is the only pattern allowed to not parse to any quad\")\n\t}\n\treturn quads, nil\n}\n\n// makeSingleEntityQuad creates a quad representing a propertyless entity. The\n// quad declares the entity is of type Resource, the base type of all entities\n// in RDF.\nfunc makeSingleEntityQuad(id quad.IRI) quad.Quad {\n\treturn quad.Quad{Subject: id, Predicate: quad.IRI(rdf.Type), Object: quad.IRI(rdfs.Resource)}\n}\n\nfunc isSingleEntityQuad(q quad.Quad) bool {\n\t// rdf:type rdfs:Resource is always true but not expressed in the graph.\n\t// it is used to specify an entity without specifying a property.\n\treturn q.Predicate == quad.IRI(rdf.Type) && q.Object == quad.IRI(rdfs.Resource)\n}\n"
  },
  {
    "path": "query/linkedql/steps/match_test.go",
    "content": "package steps\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar (\n\tns      = \"http://example.com/\"\n\talice   = quad.IRI(ns + \"alice\")\n\tlikes   = quad.IRI(ns + \"likes\")\n\tname    = quad.IRI(ns + \"name\")\n\tbob     = quad.IRI(ns + \"bob\")\n\taddress = quad.IRI(ns + \"address\")\n\tcity    = quad.IRI(ns + \"city\")\n\tstreet  = quad.IRI(ns + \"street\")\n\tcountry = quad.IRI(ns + \"country\")\n)\n\nvar patternTestCases = []struct {\n\tname     string\n\tpattern  map[string]interface{}\n\texpected *path.Path\n}{\n\t{\n\t\tname:     \"Empty Pattern\",\n\t\tpattern:  map[string]interface{}{},\n\t\texpected: path.StartMorphism(),\n\t},\n\t{\n\t\tname: \"Single Entity\",\n\t\tpattern: map[string]interface{}{\n\t\t\t\"@id\": string(alice),\n\t\t},\n\t\texpected: path.StartMorphism().Is(alice),\n\t},\n\t{\n\t\tname: \"Single Property Value\",\n\t\tpattern: map[string]interface{}{\n\t\t\tstring(likes): map[string]interface{}{\"@id\": string(bob)},\n\t\t},\n\t\texpected: path.StartMorphism().Has(likes, bob),\n\t},\n\t{\n\t\tname: \"Nested Structure\",\n\t\tpattern: map[string]interface{}{\n\t\t\tstring(address): map[string]interface{}{\n\t\t\t\tstring(street): \"Lafayette\",\n\t\t\t},\n\t\t},\n\t\texpected: path.\n\t\t\tStartMorphism().\n\t\t\tOut(address).\n\t\t\tFollow(\n\t\t\t\tpath.StartMorphism().\n\t\t\t\t\tHas(street, quad.String(\"Lafayette\")),\n\t\t\t).\n\t\t\tBack(\"\"),\n\t},\n\t{\n\t\tname: \"Two Level Nested Structure\",\n\t\tpattern: map[string]interface{}{\n\t\t\tstring(address): map[string]interface{}{\n\t\t\t\tstring(country): map[string]interface{}{\n\t\t\t\t\tstring(name): \"The United States of America\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\texpected: path.\n\t\t\tStartMorphism().\n\t\t\tOut(address).\n\t\t\tFollow(\n\t\t\t\tpath.StartMorphism().\n\t\t\t\t\tOut(country).\n\t\t\t\t\tFollow(\n\t\t\t\t\t\tpath.StartMorphism().\n\t\t\t\t\t\t\tHas(name, quad.String(\"The United States of America\")),\n\t\t\t\t\t).\n\t\t\t\t\tBack(\"\"),\n\t\t\t).\n\t\t\tBack(\"\"),\n\t},\n}\n\nfunc TestBuildPath(t *testing.T) {\n\tfor _, c := range patternTestCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tns := voc.Namespaces{}\n\t\t\tquads, err := parsePattern(c.pattern, &ns)\n\t\t\trequire.NoError(t, err)\n\t\t\tp := buildPatternPath(quads, &ns)\n\t\t\texpectedShape := c.expected.Shape()\n\t\t\tshape := p.Shape()\n\t\t\t// TODO(iddan): replace with stable comparison. Currently, it breaks\n\t\t\t// because order of properties is not guaranteed\n\t\t\trequire.Equal(t, expectedShape, shape)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/linkedql/steps/optional.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Optional{})\n}\n\nvar _ linkedql.PathStep = (*Optional)(nil)\n\n// Optional corresponds to .optional().\ntype Optional struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tStep linkedql.PathStep `json:\"step\"`\n}\n\n// Description implements Step.\nfunc (s *Optional) Description() string {\n\treturn \"attempts to follow the given path from the current entity / value, if fails the entity / value will still be kept in the results\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Optional) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp, err := s.Step.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Optional(p), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/order.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Order{})\n}\n\nvar _ linkedql.PathStep = (*Order)(nil)\n\n// Order corresponds to .order().\ntype Order struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *Order) Description() string {\n\treturn \"sorts the results in ascending order according to the current entity / value\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Order) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Order(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/out.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Out{})\n}\n\nvar _ linkedql.PathStep = (*Out)(nil)\n\n// Out is an alias for View.\ntype Out struct {\n\tVisit\n}\n\n// Description implements Step.\nfunc (s *Out) Description() string {\n\treturn \"aliases for View\"\n}\n"
  },
  {
    "path": "query/linkedql/steps/placeholder.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Placeholder{})\n}\n\nvar _ linkedql.PathStep = (*Placeholder)(nil)\n\n// Placeholder corresponds to .Placeholder().\ntype Placeholder struct{}\n\n// Description implements Step.\nfunc (s *Placeholder) Description() string {\n\treturn \"is like Vertex but resolves to the values in the context it is placed in. It should only be used where a linkedql.PathStep is expected and can't be resolved on its own.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Placeholder) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\treturn path.StartMorphism(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/properties.go",
    "content": "package steps\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Properties{})\n}\n\nvar _ linkedql.PathStep = (*Properties)(nil)\n\n// Properties corresponds to .properties().\ntype Properties struct {\n\tFrom  linkedql.PathStep      `json:\"from\"`\n\tNames *linkedql.PropertyPath `json:\"names\"`\n}\n\n// Description implements Step.\nfunc (s *Properties) Description() string {\n\treturn \"adds tags for all properties of the current entity\"\n}\n\nfunc resolveNames(names *linkedql.PropertyPath) (linkedql.PropertyIRIs, error) {\n\tif names == nil {\n\t\treturn nil, fmt.Errorf(\"Not implemented: should tag all properties\")\n\t}\n\tswitch n := names.PropertyPathI.(type) {\n\tcase linkedql.PropertyStep:\n\t\treturn nil, fmt.Errorf(\"Not implemented: should use step to resolve to properties\")\n\tcase linkedql.PropertyIRIs:\n\t\treturn n, nil\n\tcase linkedql.PropertyIRIStrings:\n\t\treturn n.PropertyIRIs(), nil\n\tcase linkedql.PropertyIRI:\n\t\treturn linkedql.PropertyIRIs{n}, nil\n\tcase linkedql.PropertyIRIString:\n\t\treturn linkedql.PropertyIRIs{linkedql.PropertyIRI(n)}, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"Unexpected type\")\n\t}\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Properties) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := fromPath\n\tnames, err := resolveNames(s.Names)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, n := range names {\n\t\tname := quad.IRI(n).FullWith(ns)\n\t\ttag := string(name)\n\t\tp = p.Save(name, tag)\n\t}\n\treturn p, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/property_names.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&PropertyNames{})\n}\n\nvar _ linkedql.PathStep = (*PropertyNames)(nil)\n\n// PropertyNames corresponds to .propertyNames().\ntype PropertyNames struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *PropertyNames) Description() string {\n\treturn \"gets the list of predicates that are pointing out from a node.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *PropertyNames) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.OutPredicates(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/property_names_as.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&PropertyNamesAs{})\n}\n\nvar _ linkedql.PathStep = (*PropertyNamesAs)(nil)\n\n// PropertyNamesAs corresponds to .propertyNamesAs().\ntype PropertyNamesAs struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tTag  string            `json:\"tag\"`\n}\n\n// Description implements Step.\nfunc (s *PropertyNamesAs) Description() string {\n\treturn \"tags the list of predicates that are pointing out from a node.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *PropertyNamesAs) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.SavePredicates(false, s.Tag), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/regexp.go",
    "content": "package steps\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&RegExp{})\n}\n\nvar _ linkedql.PathStep = (*RegExp)(nil)\n\n// RegExp corresponds to regex().\ntype RegExp struct {\n\tFrom        linkedql.PathStep `json:\"from\"`\n\tExpression  string            `json:\"expression\"`\n\tIncludeIRIs bool              `json:\"includeIRIs,omitempty\"`\n}\n\n// Description implements Step.\nfunc (s *RegExp) Description() string {\n\treturn \"RegExp filters out values that do not match given pattern. If includeIRIs is set to true it matches IRIs in addition to literals.\"\n}\n\n// BuildPath implements PathStep.\nfunc (s *RegExp) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpattern, err := regexp.Compile(s.Expression)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif s.IncludeIRIs {\n\t\treturn fromPath.RegexWithRefs(pattern), nil\n\t}\n\treturn fromPath.RegexWithRefs(pattern), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/reverse_properties.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&ReverseProperties{})\n}\n\nvar _ linkedql.PathStep = (*ReverseProperties)(nil)\n\n// ReverseProperties corresponds to .reverseProperties().\ntype ReverseProperties struct {\n\tFrom  linkedql.PathStep      `json:\"from\"`\n\tNames *linkedql.PropertyPath `json:\"names\"`\n}\n\n// Description implements Step.\nfunc (s *ReverseProperties) Description() string {\n\treturn \"gets all the properties the current entity / value is referenced at\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *ReverseProperties) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := fromPath\n\tnames, err := resolveNames(s.Names)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, n := range names {\n\t\tname := quad.IRI(n).FullWith(ns)\n\t\ttag := string(name)\n\t\tp = fromPath.SaveReverse(name, tag)\n\t}\n\treturn p, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/reverse_property_names.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&ReversePropertyNames{})\n}\n\nvar _ linkedql.PathStep = (*ReversePropertyNames)(nil)\n\n// ReversePropertyNames corresponds to .reversePropertyNames().\ntype ReversePropertyNames struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *ReversePropertyNames) Description() string {\n\treturn \"gets the list of predicates that are pointing in to a node.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *ReversePropertyNames) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.InPredicates(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/reverse_property_names_as.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&ReversePropertyNamesAs{})\n}\n\nvar _ linkedql.PathStep = (*ReversePropertyNamesAs)(nil)\n\n// ReversePropertyNamesAs corresponds to .reversePropertyNamesAs().\ntype ReversePropertyNamesAs struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n\tTag  string            `json:\"tag\"`\n}\n\n// Description implements Step.\nfunc (s *ReversePropertyNamesAs) Description() string {\n\treturn \"tags the list of predicates that are pointing in to a node.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *ReversePropertyNamesAs) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.SavePredicates(true, s.Tag), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/skip.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Skip{})\n}\n\nvar _ linkedql.PathStep = (*Skip)(nil)\n\n// Skip corresponds to .skip().\ntype Skip struct {\n\tFrom   linkedql.PathStep `json:\"from\"`\n\tOffset int64             `json:\"offset\"`\n}\n\n// Description implements Step.\nfunc (s *Skip) Description() string {\n\treturn \"skips a number of nodes for current path.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Skip) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Skip(s.Offset), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/steps_final.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Select{})\n\tlinkedql.Register(&Documents{})\n}\n\nvar _ linkedql.IteratorStep = (*Select)(nil)\n\n// Select corresponds to .select().\ntype Select struct {\n\tProperties []string          `json:\"properties\"`\n\tFrom       linkedql.PathStep `json:\"from\"`\n\tExcludeID  bool              `json:\"excludeID\"`\n}\n\n// Description implements Step.\nfunc (s *Select) Description() string {\n\treturn \"Select returns flat records of tags matched in the query\"\n}\n\n// BuildIterator implements IteratorStep\nfunc (s *Select) BuildIterator(qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error) {\n\tvalueIt, err := linkedql.NewValueIteratorFromPathStep(s.From, qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tit := linkedql.NewTagsIterator(valueIt, s.Properties, s.ExcludeID)\n\treturn &it, nil\n}\n\nvar _ linkedql.IteratorStep = (*Documents)(nil)\n\n// Documents corresponds to .documents().\ntype Documents struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *Documents) Description() string {\n\treturn \"Documents return documents of the tags matched in the query associated with their entity\"\n}\n\n// BuildIterator implements IteratorStep\nfunc (s *Documents) BuildIterator(qs graph.QuadStore, ns *voc.Namespaces) (query.Iterator, error) {\n\tp, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tit, err := linkedql.NewValueIterator(p, qs), nil\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn linkedql.NewDocumentIterator(it), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/steps_test.go",
    "content": "package steps\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/cayleygraph/quad/voc\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype TestCase struct {\n\tData    interface{} `json:\"data\"`\n\tQuery   interface{} `json:\"query\"`\n\tResults interface{} `json:\"results\"`\n}\n\nfunc readData(data interface{}) ([]quad.Quad, error) {\n\td, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb := bytes.NewBuffer(d)\n\treader := jsonld.NewReader(b)\n\tquads, err := quad.ReadAll(reader)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn quads, nil\n}\n\nfunc readQuery(raw interface{}) (linkedql.Step, error) {\n\td, err := json.Marshal(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tq, err := linkedql.Unmarshal(d)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tquery, ok := q.(linkedql.Step)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Expected linkedql.Step\")\n\t}\n\treturn query, nil\n}\n\nfunc TestLinkedQL(t *testing.T) {\n\t// Using files\n\tdirectory := \"test-cases\"\n\tfiles, err := ioutil.ReadDir(directory)\n\tif err != nil {\n\t\trequire.NoError(t, err)\n\t}\n\tfor _, info := range files {\n\t\tfileName := info.Name()\n\t\tfilePath := filepath.Join(directory, fileName)\n\n\t\tif !strings.HasSuffix(fileName, \".json\") {\n\t\t\t// skip non-JSON files\n\t\t\tcontinue\n\t\t}\n\n\t\ttestName := strings.TrimSuffix(fileName, filepath.Ext(fileName))\n\t\tt.Run(testName, func(t *testing.T) {\n\t\t\tfile, err := ioutil.ReadFile(filePath)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar c TestCase\n\t\t\terr = json.Unmarshal(file, &c)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tdata, err := readData(c.Data)\n\t\t\trequire.NoError(t, err, fileName)\n\t\t\trequire.NotEmpty(t, data, fileName)\n\n\t\t\tquery, err := readQuery(c.Query)\n\t\t\trequire.NoError(t, err, fileName)\n\t\t\trequire.NotNil(t, query, fileName)\n\t\t\tstore := memstore.New(data...)\n\t\t\tvoc := voc.Namespaces{}\n\t\t\tctx := context.TODO()\n\t\t\titerator, err := linkedql.BuildIterator(query, store, &voc)\n\t\t\trequire.NoError(t, err)\n\t\t\tvar results []interface{}\n\t\t\tfor iterator.Next(ctx) {\n\t\t\t\tresults = append(results, iterator.Result())\n\t\t\t}\n\t\t\trequire.NoError(t, iterator.Err())\n\t\t\trequire.Equal(t, nil, isomorphic(c.Results, results))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/all-vertices.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Vertex\",\n    \"values\": []\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/likes\" },\n    { \"@id\": \"http://example.com/bob\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/back.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Back\",\n    \"from\": {\n      \"@type\": \"Visit\",\n      \"from\": {\n        \"@type\": \"Match\",\n        \"pattern\": { \"@id\": \"http://example.com/alice\" }\n      },\n      \"properties\": [\"http://example.com/likes\"]\n    },\n    \"name\": \"\"\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/both.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@graph\": [\n      { \"@id\": \"bob\", \"likes\": { \"@id\": \"alice\" } },\n      { \"@id\": \"dan\", \"likes\": { \"@id\": \"bob\" } }\n    ]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Both\",\n    \"from\": {\n      \"@type\": \"Match\",\n      \"pattern\": { \"@id\": \"http://example.com/bob\" }\n    },\n    \"properties\": \"http://example.com/likes\"\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/dan\" },\n    { \"@id\": \"http://example.com/alice\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/collect.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"friends\": { \"@list\": [{ \"@id\": \"alice\" }, { \"@id\": \"bob\" }] }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Documents\",\n    \"from\": {\n      \"@type\": \"Collect\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"name\": \"http://example.com/friends\"\n    }\n  },\n  \"results\": [\n    {\n      \"http://example.com/friends\": {\n        \"@list\": [\n          { \"@id\": \"http://example.com/alice\" },\n          { \"@id\": \"http://example.com/bob\" }\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/count.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Count\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} }\n  },\n  \"results\": [4]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/difference.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Difference\",\n    \"from\": {\n      \"@type\": \"Vertex\",\n      \"values\": [\n        { \"@id\": \"http://example.com/alice\" },\n        { \"@id\": \"http://example.com/likes\" }\n      ]\n    },\n    \"steps\": [\n      { \"@type\": \"Match\", \"pattern\": { \"@id\": \"http://example.com/likes\" } }\n    ]\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/documents.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@graph\": [\n      { \"@id\": \"alice\", \"likes\": { \"@id\": \"bob\" }, \"name\": \"Alice\" },\n      { \"@id\": \"bob\", \"likes\": { \"@id\": \"alice\" }, \"name\": \"Bob\" }\n    ]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Documents\",\n    \"from\": {\n      \"@type\": \"Properties\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"names\": [\"http://example.com/name\", \"http://example.com/likes\"]\n    }\n  },\n  \"results\": {\n    \"@graph\": [\n      {\n        \"@id\": \"http://example.com/alice\",\n        \"http://example.com/likes\": [{ \"@id\": \"http://example.com/bob\" }],\n        \"http://example.com/name\": [\"Alice\"]\n      },\n      {\n        \"@id\": \"http://example.com/bob\",\n        \"http://example.com/likes\": [{ \"@id\": \"http://example.com/alice\" }],\n        \"http://example.com/name\": [\"Bob\"]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/greater-than-equals.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": [0, 1, 2]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"GreaterThanEquals\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"value\": 1\n  },\n  \"results\": [1, 2]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/greater-than.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": [0, 1]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"GreaterThan\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"value\": 0\n  },\n  \"results\": [1]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/has-reverse.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"HasReverse\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"property\": \"http://example.com/likes\",\n    \"values\": [{ \"@id\": \"http://example.com/alice\" }]\n  },\n  \"results\": [{ \"@id\": \"http://example.com/bob\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/has.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Has\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"property\": \"http://example.com/likes\",\n    \"values\": [{ \"@id\": \"http://example.com/bob\" }]\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/intersect.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@graph\": [\n      { \"@id\": \"bob\", \"likes\": { \"@id\": \"alice\" } },\n      { \"@id\": \"dan\", \"likes\": { \"@id\": \"alice\" } }\n    ]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Intersect\",\n    \"from\": {\n      \"@type\": \"Visit\",\n      \"from\": {\n        \"@type\": \"Match\",\n        \"pattern\": { \"@id\": \"http://example.com/bob\" }\n      },\n      \"properties\": \"http://example.com/likes\"\n    },\n    \"steps\": [\n      {\n        \"@type\": \"Visit\",\n        \"from\": {\n          \"@type\": \"Match\",\n          \"values\": { \"@id\": \"http://example.com/bob\" }\n        },\n        \"properties\": \"http://example.com/likes\"\n      }\n    ]\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/less-than-equals.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": [-1, 0, 1]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"LessThanEquals\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"value\": 0\n  },\n  \"results\": [-1, 0]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/less-than.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": [0, 1]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"LessThan\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"value\": 1\n  },\n  \"results\": [0]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/like.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": \"Alice\"\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Like\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"likePattern\": \"%ali%\"\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/limit.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Limit\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"limit\": 2\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/likes\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/match-all.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Match\",\n    \"pattern\": {}\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/likes\" },\n    { \"@id\": \"http://example.com/bob\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/match-exact.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Match\",\n    \"pattern\": { \"@id\": \"http://example.com/alice\" }\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/optional.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@graph\": [\n      { \"@id\": \"alice\", \"likes\": { \"@id\": \"bob\" }, \"name\": \"Alice\" },\n      { \"@id\": \"bob\", \"name\": \"Bob\" }\n    ]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"Optional\",\n      \"from\": {\n        \"@type\": \"Properties\",\n        \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n        \"names\": [\"http://example.com/name\"]\n      },\n      \"step\": {\n        \"@type\": \"Properties\",\n        \"from\": { \"@type\": \"Placeholder\" },\n        \"names\": [\"http://example.com/likes\"]\n      }\n    },\n    \"tags\": []\n  },\n  \"results\": [\n    {\n      \"http://example.com/likes\": { \"@id\": \"http://example.com/bob\" },\n      \"http://example.com/name\": \"Alice\"\n    },\n    { \"http://example.com/name\": \"Bob\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/order.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Order\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} }\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/bob\" },\n    { \"@id\": \"http://example.com/likes\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/properties.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"Properties\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"names\": [\"http://example.com/likes\"]\n    },\n    \"tags\": []\n  },\n  \"results\": [\n    { \"http://example.com/likes\": { \"@id\": \"http://example.com/bob\" } }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/property-names-as.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"PropertyNamesAs\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"tag\": \"property\"\n    },\n    \"tags\": []\n  },\n  \"results\": [{ \"property\": { \"@id\": \"http://example.com/likes\" } }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/property-names.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"PropertyNames\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} }\n  },\n  \"results\": [{ \"@id\": \"http://example.com/likes\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/reg-exp.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"name\": \"Alice\"\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"RegExp\",\n    \"expression\": \"A\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"includeIRIs\": false\n  },\n  \"results\": [\"Alice\"]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/reverse-properties.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"ReverseProperties\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"names\": [\"http://example.com/likes\"]\n    },\n    \"tags\": []\n  },\n  \"results\": [\n    { \"http://example.com/likes\": { \"@id\": \"http://example.com/alice\" } }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/reverse-property-names-as.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"ReversePropertyNamesAs\",\n      \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n      \"tag\": \"property\"\n    },\n    \"tags\": []\n  },\n  \"results\": [{ \"property\": { \"@id\": \"http://example.com/likes\" } }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/select-with-tags.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"As\",\n      \"from\": {\n        \"@type\": \"Visit\",\n        \"from\": {\n          \"@type\": \"As\",\n          \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n          \"name\": \"http://example.com/liker\"\n        },\n        \"properties\": \"http://example.com/likes\"\n      },\n      \"name\": \"liked\"\n    },\n    \"tags\": [\"http://example.com/liker\"]\n  },\n  \"results\": [\n    { \"http://example.com/liker\": { \"@id\": \"http://example.com/alice\" } }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/select.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"Visit\",\n      \"from\": {\n        \"@type\": \"As\",\n        \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n        \"name\": \"http://example.com/liker\"\n      },\n      \"properties\": \"http://example.com/likes\"\n    },\n    \"tags\": []\n  },\n  \"results\": [\n    { \"http://example.com/liker\": { \"@id\": \"http://example.com/alice\" } }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/skip.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Skip\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"offset\": 2\n  },\n  \"results\": [{ \"@id\": \"http://example.com/bob\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/union.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Union\",\n    \"from\": {\n      \"@type\": \"Match\",\n      \"pattern\": { \"@id\": \"http://example.com/alice\" }\n    },\n    \"steps\": [\n      { \"@type\": \"Match\", \"pattern\": { \"@id\": \"http://example.com/bob\" } }\n    ]\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/bob\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/unique.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Unique\",\n    \"from\": {\n      \"@type\": \"Vertex\",\n      \"values\": [\n        { \"@id\": \"http://example.com/alice\" },\n        { \"@id\": \"http://example.com/alice\" },\n        { \"@id\": \"http://example.com/bob\" }\n      ]\n    }\n  },\n  \"results\": [\n    { \"@id\": \"http://example.com/alice\" },\n    { \"@id\": \"http://example.com/bob\" }\n  ]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/view-reverse.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"VisitReverse\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"properties\": \"http://example.com/likes\"\n  },\n  \"results\": [{ \"@id\": \"http://example.com/alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/view.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@id\": \"alice\",\n    \"likes\": { \"@id\": \"bob\" }\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Visit\",\n    \"from\": { \"@type\": \"Match\", \"pattern\": {} },\n    \"properties\": \"http://example.com/likes\"\n  },\n  \"results\": [{ \"@id\": \"http://example.com/bob\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/test-cases/where.json",
    "content": "{\n  \"data\": {\n    \"@context\": {\n      \"@base\": \"http://example.com/\",\n      \"@vocab\": \"http://example.com/\"\n    },\n    \"@graph\": [\n      { \"@id\": \"alice\", \"likes\": { \"@id\": \"bob\" }, \"name\": \"Alice\" },\n      { \"@id\": \"bob\", \"name\": \"Bob\" }\n    ]\n  },\n  \"query\": {\n    \"@context\": { \"@vocab\": \"http://cayley.io/linkedql#\" },\n    \"@type\": \"Select\",\n    \"from\": {\n      \"@type\": \"Properties\",\n      \"from\": {\n        \"@type\": \"Where\",\n        \"condition\": {\n          \"@type\": \"Has\",\n          \"from\": { \"@type\": \"Placeholder\" },\n          \"property\": \"http://example.com/likes\",\n          \"values\": [{ \"@id\": \"http://example.com/bob\" }]\n        },\n        \"from\": { \"@type\": \"Match\", \"pattern\": {} }\n      },\n      \"names\": [\"http://example.com/name\"]\n    },\n    \"tags\": []\n  },\n  \"results\": [{ \"http://example.com/name\": \"Alice\" }]\n}\n"
  },
  {
    "path": "query/linkedql/steps/union.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Union{})\n}\n\nvar _ linkedql.PathStep = (*Union)(nil)\n\n// Union corresponds to .union() and .or().\ntype Union struct {\n\tFrom  linkedql.PathStep   `json:\"from\"`\n\tSteps []linkedql.PathStep `json:\"steps\"`\n}\n\n// Description implements Step.\nfunc (s *Union) Description() string {\n\treturn \"returns the combined paths of the two queries. Notice that it's per-path, not per-node. Once again, if multiple paths reach the same destination, they might have had different ways of getting there (and different tags).\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Union) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp := fromPath\n\tfor _, step := range s.Steps {\n\t\tvaluePath, err := step.BuildPath(qs, ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp = p.Or(valuePath)\n\t}\n\treturn p, nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/unique.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Unique{})\n}\n\nvar _ linkedql.PathStep = (*Unique)(nil)\n\n// Unique corresponds to .unique().\ntype Unique struct {\n\tFrom linkedql.PathStep `json:\"from\"`\n}\n\n// Description implements Step.\nfunc (s *Unique) Description() string {\n\treturn \"removes duplicate values from the path.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Unique) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Unique(), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/vertex.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Vertex{})\n}\n\nvar _ linkedql.PathStep = (*Vertex)(nil)\n\n// Vertex corresponds to g.Vertex() and g.V().\ntype Vertex struct {\n\tValues []quad.Value `json:\"values\"`\n}\n\n// Description implements Step.\nfunc (s *Vertex) Description() string {\n\treturn \"resolves to all the existing objects and primitive values in the graph. If provided with values resolves to a sublist of all the existing values in the graph.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Vertex) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\treturn path.StartPath(qs, linkedql.AbsoluteValues(s.Values, ns)...), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/visit.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Visit{})\n}\n\nvar _ linkedql.PathStep = (*Visit)(nil)\n\n// Visit corresponds to .view().\ntype Visit struct {\n\tFrom       linkedql.PathStep      `json:\"from\"`\n\tProperties *linkedql.PropertyPath `json:\"properties\"`\n}\n\n// Description implements Step.\nfunc (s *Visit) Description() string {\n\treturn \"resolves to the values of the given property or properties in via of the current objects. If via is a path it's resolved values will be used as properties.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Visit) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tviaPath, err := s.Properties.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.Out(viaPath), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/visit_reverse.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&VisitReverse{})\n}\n\nvar _ linkedql.PathStep = (*VisitReverse)(nil)\n\n// VisitReverse corresponds to .viewReverse().\ntype VisitReverse struct {\n\tFrom       linkedql.PathStep      `json:\"from\"`\n\tProperties *linkedql.PropertyPath `json:\"properties\"`\n}\n\n// Description implements Step.\nfunc (s *VisitReverse) Description() string {\n\treturn \"is the inverse of View. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects.\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *VisitReverse) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tviaPath, err := s.Properties.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.In(viaPath), nil\n}\n"
  },
  {
    "path": "query/linkedql/steps/where.go",
    "content": "package steps\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/linkedql\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc init() {\n\tlinkedql.Register(&Where{})\n}\n\nvar _ linkedql.PathStep = (*Where)(nil)\n\n// Where corresponds to .where().\ntype Where struct {\n\tFrom      linkedql.PathStep `json:\"from\"`\n\tCondition linkedql.PathStep `json:\"condition\"`\n}\n\n// Description implements Step.\nfunc (s *Where) Description() string {\n\treturn \"filters results that fulfill a specified condition\"\n}\n\n// BuildPath implements linkedql.PathStep.\nfunc (s *Where) BuildPath(qs graph.QuadStore, ns *voc.Namespaces) (*path.Path, error) {\n\tfromPath, err := s.From.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tstepPath, err := s.Condition.BuildPath(qs, ns)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fromPath.And(stepPath.Reverse()), nil\n}\n"
  },
  {
    "path": "query/linkedql/voc_util.go",
    "content": "package linkedql\n\nimport (\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\n// AbsoluteValue uses given ns to resolve short IRIs and types in typed strings to their full form\nfunc AbsoluteValue(value quad.Value, ns *voc.Namespaces) quad.Value {\n\tswitch v := value.(type) {\n\tcase quad.IRI:\n\t\treturn v.FullWith(ns)\n\tcase quad.TypedString:\n\t\treturn quad.TypedString{Value: v.Value, Type: v.Type.FullWith(ns)}\n\tdefault:\n\t\treturn v\n\t}\n}\n\n// AbsoluteValues applies AbsoluteValue on each item in provided values using provided ns\nfunc AbsoluteValues(values []quad.Value, ns *voc.Namespaces) []quad.Value {\n\tvar absoluteValues []quad.Value\n\tfor _, value := range values {\n\t\tabsoluteValues = append(absoluteValues, AbsoluteValue(value, ns))\n\t}\n\treturn absoluteValues\n}\n"
  },
  {
    "path": "query/mql/build_iterator.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage mql\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc buildFixed(s string) shape.Shape {\n\treturn shape.Lookup{quad.StringToValue(s)}\n}\n\nfunc buildAllResult(path Path) shape.Shape {\n\treturn shape.Save{\n\t\tFrom: shape.AllNodes{},\n\t\tTags: []string{string(path)},\n\t}\n}\n\nfunc (q *Query) BuildIteratorTree(ctx context.Context, query interface{}) {\n\tq.isRepeated = make(map[Path]bool)\n\tq.queryStructure = make(map[Path]map[string]interface{})\n\tq.queryResult = make(map[ResultPath]map[string]interface{})\n\tq.queryResult[\"\"] = make(map[string]interface{})\n\n\tvar (\n\t\topt bool\n\t\ts   shape.Shape\n\t)\n\ts, opt, q.err = q.buildShape(query, NewPath())\n\tif q.err == nil && opt {\n\t\tq.err = errors.New(\"optional iterator at the top level\")\n\t}\n\tq.it = shape.BuildIterator(ctx, q.ses.qs, s)\n}\n\nfunc (q *Query) buildShape(query interface{}, path Path) (s shape.Shape, optional bool, err error) {\n\terr = nil\n\toptional = false\n\tswitch t := query.(type) {\n\tcase bool:\n\t\t// for JSON booleans\n\t\ts = shape.Lookup{quad.Bool(t)}\n\tcase float64:\n\t\t// for JSON numbers\n\t\t// Damn you, Javascript, and your lack of integer values.\n\t\tif math.Floor(t) == t {\n\t\t\t// Treat it like an integer.\n\t\t\ts = shape.Lookup{quad.Int(t)}\n\t\t} else {\n\t\t\ts = shape.Lookup{quad.Float(t)}\n\t\t}\n\tcase string:\n\t\t// for JSON strings\n\t\ts = buildFixed(t)\n\tcase []interface{}:\n\t\t// for JSON arrays\n\t\tq.isRepeated[path] = true\n\t\tif len(t) == 0 {\n\t\t\ts = buildAllResult(path)\n\t\t\toptional = true\n\t\t} else if len(t) == 1 {\n\t\t\ts, optional, err = q.buildShape(t[0], path)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"multiple fields at location root %s\", path.DisplayString())\n\t\t}\n\tcase map[string]interface{}:\n\t\t// for JSON objects\n\t\ts, err = q.buildShapeMap(t, path)\n\tcase nil:\n\t\ts = buildAllResult(path)\n\t\toptional = true\n\tdefault:\n\t\terr = fmt.Errorf(\"Unknown JSON type: %T\", query)\n\t}\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\ts = shape.Save{\n\t\tFrom: s,\n\t\tTags: []string{string(path)},\n\t}\n\treturn s, optional, nil\n}\n\nfunc (q *Query) buildShapeMap(query map[string]interface{}, path Path) (shape.Shape, error) {\n\tit := shape.IntersectOpt{\n\t\tSub: shape.Intersect{\n\t\t\tshape.AllNodes{},\n\t\t},\n\t}\n\toutputStructure := make(map[string]interface{})\n\tfor key, subquery := range query {\n\t\toptional := false\n\t\toutputStructure[key] = nil\n\t\treverse := false\n\t\tpred := key\n\t\tif strings.HasPrefix(pred, \"@\") {\n\t\t\ti := strings.Index(pred, \":\")\n\t\t\tif i != -1 {\n\t\t\t\tpred = pred[(i + 1):]\n\t\t\t}\n\t\t}\n\t\tif strings.HasPrefix(pred, \"!\") {\n\t\t\treverse = true\n\t\t\tpred = strings.TrimPrefix(pred, \"!\")\n\t\t}\n\n\t\t// Other special constructs here\n\t\tvar subit shape.Shape\n\t\tif key == \"id\" {\n\t\t\tvar err error\n\t\t\tsubit, optional, err = q.buildShape(subquery, path.Follow(key))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\tvar (\n\t\t\t\tbuiltIt shape.Shape\n\t\t\t\terr     error\n\t\t\t)\n\t\t\tbuiltIt, optional, err = q.buildShape(subquery, path.Follow(key))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfrom, to := quad.Subject, quad.Object\n\t\t\tif reverse {\n\t\t\t\tfrom, to = to, from\n\t\t\t}\n\t\t\tsubit = shape.NodesFrom{\n\t\t\t\tDir: from,\n\t\t\t\tQuads: shape.Quads{\n\t\t\t\t\t{Dir: quad.Predicate, Values: buildFixed(pred)},\n\t\t\t\t\t{Dir: to, Values: builtIt},\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\tif optional {\n\t\t\tit.AddOptional(subit)\n\t\t} else {\n\t\t\tit.Add(subit)\n\t\t}\n\t}\n\tq.queryStructure[path] = outputStructure\n\tif len(it.Opt) == 0 {\n\t\treturn it.Sub, nil\n\t}\n\treturn it, nil\n}\n\ntype byRecordLength []ResultPath\n\nfunc (p byRecordLength) Len() int {\n\treturn len(p)\n}\n\nfunc (p byRecordLength) Less(i, j int) bool {\n\tiLen := len(strings.Split(string(p[i]), \"\\x30\"))\n\tjLen := len(strings.Split(string(p[j]), \"\\x30\"))\n\tif iLen < jLen {\n\t\treturn true\n\t}\n\tif iLen == jLen {\n\t\tif len(string(p[i])) < len(string(p[j])) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p byRecordLength) Swap(i, j int) {\n\tp[i], p[j] = p[j], p[i]\n}\n"
  },
  {
    "path": "query/mql/fill.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage mql\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc (q *Query) treeifyResult(tags map[string]graph.Ref) (map[ResultPath]string, error) {\n\t// Transform the map into something a little more interesting.\n\tresults := make(map[Path]string)\n\tfor k, v := range tags {\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tnv, err := q.ses.qs.NameOf(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults[Path(k)] = quadValueToNative(nv)\n\t}\n\tresultPaths := make(map[ResultPath]string)\n\tfor k, v := range results {\n\t\tresultPaths[k.ToResultPathFromMap(results)] = v\n\t}\n\n\tpaths := make([]ResultPath, 0, len(resultPaths))\n\tfor path := range resultPaths {\n\t\tpaths = append(paths, path)\n\t}\n\tsort.Sort(byRecordLength(paths))\n\n\t// Build Structure\n\tfor _, path := range paths {\n\t\tcurrentPath := path.getPath()\n\t\tvalue := resultPaths[path]\n\t\tnamePath := path.AppendValue(value)\n\t\tif _, ok := q.queryResult[namePath]; !ok {\n\t\t\ttargetPath, key := path.splitLastPath()\n\t\t\tif path == \"\" {\n\t\t\t\ttargetPath, key = \"\", value\n\t\t\t\tif _, ok := q.queryResult[\"\"][value]; !ok {\n\t\t\t\t\tq.resultOrder = append(q.resultOrder, value)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif _, ok := q.queryStructure[currentPath]; ok {\n\t\t\t\t// If there's substructure, then copy that in.\n\t\t\t\tnewStruct := q.copyPathStructure(currentPath)\n\t\t\t\tif q.isRepeated[currentPath] && currentPath != \"\" {\n\t\t\t\t\tswitch t := q.queryResult[targetPath][key].(type) {\n\t\t\t\t\tcase nil:\n\t\t\t\t\t\tx := make([]interface{}, 0)\n\t\t\t\t\t\tx = append(x, newStruct)\n\t\t\t\t\t\tq.queryResult[targetPath][key] = x\n\t\t\t\t\t\tq.queryResult[namePath] = newStruct\n\t\t\t\t\tcase []interface{}:\n\t\t\t\t\t\tq.queryResult[targetPath][key] = append(t, newStruct)\n\t\t\t\t\t\tq.queryResult[namePath] = newStruct\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\t\t\t\t\tq.queryResult[namePath] = newStruct\n\t\t\t\t\tq.queryResult[targetPath][key] = newStruct\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fill values\n\tfor _, path := range paths {\n\t\tcurrentPath := path.getPath()\n\t\tvalue, ok := resultPaths[path]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tnamePath := path.AppendValue(value)\n\t\tif _, ok := q.queryStructure[currentPath]; ok {\n\t\t\t// We're dealing with ids.\n\t\t\tif _, ok := q.queryResult[namePath][\"id\"]; ok {\n\t\t\t\tq.queryResult[namePath][\"id\"] = value\n\t\t\t}\n\t\t} else {\n\t\t\t// Just a value.\n\t\t\ttargetPath, key := path.splitLastPath()\n\t\t\tif q.isRepeated[currentPath] {\n\t\t\t\tswitch t := q.queryResult[targetPath][key].(type) {\n\t\t\t\tcase nil:\n\t\t\t\t\tx := make([]interface{}, 0)\n\t\t\t\t\tx = append(x, value)\n\t\t\t\t\tq.queryResult[targetPath][key] = x\n\t\t\t\tcase []interface{}:\n\t\t\t\t\tq.queryResult[targetPath][key] = append(t, value)\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tq.queryResult[targetPath][key] = value\n\t\t\t}\n\t\t}\n\t}\n\n\treturn resultPaths, nil\n}\n\nfunc (q *Query) buildResults() {\n\tfor _, v := range q.resultOrder {\n\t\tq.results = append(q.results, q.queryResult[\"\"][v])\n\t}\n}\n\nfunc quadValueToNative(v quad.Value) string {\n\tout := quad.NativeOf(v)\n\tif nv, ok := out.(quad.Value); ok && v == nv {\n\t\treturn quad.StringOf(v)\n\t}\n\treturn fmt.Sprint(out)\n}\n"
  },
  {
    "path": "query/mql/mql_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage mql\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// This is a simple test graph.\n//\n//    +---+                        +---+\n//    | A |-------               ->| F |<--\n//    +---+       \\------>+---+-/  +---+   \\--+---+\n//                 ------>|#B#|      |        | E |\n//    +---+-------/      >+---+      |        +---+\n//    | C |             /            v\n//    +---+           -/           +---+\n//      ----    +---+/             |#G#|\n//          \\-->|#D#|------------->+---+\n//              +---+\n//\n\nfunc makeTestSession(data []quad.Quad) *Session {\n\tqs, _ := graph.NewQuadStore(\"memstore\", \"\", nil)\n\tw, _ := graph.NewQuadWriter(\"single\", qs, nil)\n\tfor _, t := range data {\n\t\tw.AddQuad(t)\n\t}\n\treturn NewSession(qs)\n}\n\nvar testQueries = []struct {\n\tmessage string\n\tquery   string\n\ttag     string\n\texpect  string\n}{\n\t{\n\t\tmessage: \"get all IDs in the database\",\n\t\tquery:   `[{\"id\": null}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<alice>\"},\n\t\t\t\t{\"id\": \"<follows>\"},\n\t\t\t\t{\"id\": \"<bob>\"},\n\t\t\t\t{\"id\": \"<fred>\"},\n\t\t\t\t{\"id\": \"<status>\"},\n\t\t\t\t{\"id\": \"cool_person\"},\n\t\t\t\t{\"id\": \"<dani>\"},\n\t\t\t\t{\"id\": \"<charlie>\"},\n\t\t\t\t{\"id\": \"<greg>\"},\n\t\t\t\t{\"id\": \"<emily>\"},\n\t\t\t\t{\"id\": \"<predicates>\"},\n\t\t\t\t{\"id\": \"<are>\"},\n\t\t\t\t{\"id\": \"smart_person\"},\n\t\t\t\t{\"id\": \"<smart_graph>\"}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get nodes by status\",\n\t\tquery:   `[{\"id\": null, \"<status>\": \"cool_person\"}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<bob>\", \"<status>\": \"cool_person\"},\n\t\t\t\t{\"id\": \"<dani>\", \"<status>\": \"cool_person\"},\n\t\t\t\t{\"id\": \"<greg>\", \"<status>\": \"cool_person\"}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"show correct null semantics\",\n\t\tquery:   `[{\"id\": \"cool_person\", \"status\": null}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"cool_person\", \"status\": null}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct follows list\",\n\t\tquery:   `[{\"id\": \"<charlie>\", \"<follows>\": []}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<charlie>\", \"<follows>\": [\"<bob>\", \"<dani>\"]}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct reverse follows list\",\n\t\tquery:   `[{\"id\": \"<fred>\", \"!<follows>\": []}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<fred>\", \"!<follows>\": [\"<bob>\", \"<emily>\"]}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct follows struct\",\n\t\tquery:   `[{\"id\": null, \"<follows>\": {\"id\": null, \"<status>\": \"cool_person\"}}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<alice>\", \"<follows>\": {\"id\": \"<bob>\", \"<status>\": \"cool_person\"}},\n\t\t\t\t{\"id\": \"<dani>\", \"<follows>\": {\"id\": \"<greg>\", \"<status>\": \"cool_person\"}},\n\t\t\t\t{\"id\": \"<charlie>\", \"<follows>\": {\"id\": \"<dani>\", \"<status>\": \"cool_person\"}},\n\t\t\t\t{\"id\": \"<fred>\", \"<follows>\": {\"id\": \"<greg>\", \"<status>\": \"cool_person\"}}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct reverse follows struct\",\n\t\tquery:   `[{\"id\": null, \"!<follows>\": [{\"id\": null, \"<status>\" : \"cool_person\"}]}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<fred>\", \"!<follows>\": [{\"id\": \"<bob>\", \"<status>\": \"cool_person\"}]},\n\t\t\t\t{\"id\": \"<bob>\", \"!<follows>\": [{\"id\": \"<dani>\", \"<status>\": \"cool_person\"}]},\n\t\t\t\t{\"id\": \"<greg>\", \"!<follows>\": [{\"id\": \"<dani>\", \"<status>\": \"cool_person\"}]}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct co-follows\",\n\t\tquery:   `[{\"id\": null, \"@A:<follows>\": \"<bob>\", \"@B:<follows>\": \"<dani>\"}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<charlie>\", \"@A:<follows>\": \"<bob>\", \"@B:<follows>\": \"<dani>\"}\n\t\t\t]\n\t\t`,\n\t},\n\t{\n\t\tmessage: \"get correct reverse co-follows\",\n\t\tquery:   `[{\"id\": null, \"!<follows>\": {\"id\": \"<charlie>\"}, \"@A:!<follows>\": \"<dani>\"}]`,\n\t\texpect: `\n\t\t\t[\n\t\t\t\t{\"id\": \"<bob>\", \"!<follows>\": {\"id\": \"<charlie>\"}, \"@A:!<follows>\": \"<dani>\"}\n\t\t\t]\n\t\t`,\n\t},\n}\n\nfunc runQuery(t testing.TB, g []quad.Quad, qu string) interface{} {\n\ts := makeTestSession(g)\n\tctx := context.TODO()\n\tit, err := s.Execute(ctx, qu, query.Options{Collation: query.JSON})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer it.Close()\n\tvar out []interface{}\n\tfor it.Next(ctx) {\n\t\tout = append(out, it.Result())\n\t}\n\treturn out\n}\n\nfunc TestMQL(t *testing.T) {\n\tsimpleGraph := testutil.LoadGraph(t, \"../../data/testdata.nq\")\n\tfor _, test := range testQueries {\n\t\tt.Run(test.message, func(t *testing.T) {\n\t\t\tgot := runQuery(t, simpleGraph, test.query)\n\t\t\tvar expect interface{}\n\t\t\tjson.Unmarshal([]byte(test.expect), &expect)\n\t\t\trequire.Equal(t, expect, got)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/mql/query.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage mql\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\ntype (\n\tPath       string\n\tResultPath string\n)\n\ntype Query struct {\n\tses            *Session\n\tit             iterator.Shape\n\tisRepeated     map[Path]bool\n\tqueryStructure map[Path]map[string]interface{}\n\tqueryResult    map[ResultPath]map[string]interface{}\n\tresults        []interface{}\n\tresultOrder    []string\n\terr            error\n}\n\nfunc (q *Query) isError() bool {\n\treturn q.err != nil\n}\n\nfunc (q *Query) copyPathStructure(path Path) map[string]interface{} {\n\toutput := make(map[string]interface{})\n\tfor k, v := range q.queryStructure[path] {\n\t\toutput[k] = v\n\t}\n\treturn output\n}\n\nfunc NewPath() Path {\n\treturn \"\"\n}\nfunc (p Path) Follow(s string) Path {\n\treturn Path(fmt.Sprintf(\"%s\\x1E%s\", p, s))\n}\n\nfunc (p Path) DisplayString() string {\n\treturn strings.Replace(string(p), \"\\x1E\", \".\", -1)\n}\n\nfunc NewResultPath() ResultPath {\n\treturn \"\"\n}\n\nfunc (p ResultPath) FollowPath(followPiece string, value string) ResultPath {\n\tif string(p) == \"\" {\n\t\treturn ResultPath(fmt.Sprintf(\"%s\\x1E%s\", value, followPiece))\n\t}\n\treturn ResultPath(fmt.Sprintf(\"%s\\x1E%s\\x1E%s\", p, value, followPiece))\n}\n\nfunc (p ResultPath) getPath() Path {\n\tout := NewPath()\n\tpathPieces := strings.Split(string(p), \"\\x1E\")\n\tfor len(pathPieces) > 1 {\n\t\ta := pathPieces[1]\n\t\tpathPieces = pathPieces[2:]\n\t\tout = out.Follow(a)\n\t}\n\treturn out\n}\n\nfunc (p ResultPath) splitLastPath() (ResultPath, string) {\n\tpathPieces := strings.Split(string(p), \"\\x1E\")\n\treturn ResultPath(strings.Join(pathPieces[:len(pathPieces)-1], \"\\x1E\")), pathPieces[len(pathPieces)-1]\n}\n\nfunc (p ResultPath) AppendValue(value string) ResultPath {\n\tif string(p) == \"\" {\n\t\treturn ResultPath(value)\n\t}\n\treturn ResultPath(fmt.Sprintf(\"%s\\x1E%s\", p, value))\n}\n\nfunc (p Path) ToResultPathFromMap(resultMap map[Path]string) ResultPath {\n\toutput := NewResultPath()\n\tpathPieces := strings.Split(string(p), \"\\x1E\")[1:]\n\tpathSoFar := NewPath()\n\tfor _, piece := range pathPieces {\n\t\toutput = output.FollowPath(piece, resultMap[pathSoFar])\n\t\tpathSoFar = pathSoFar.Follow(piece)\n\t}\n\treturn output\n}\n\nfunc NewQuery(ses *Session) *Query {\n\tvar q Query\n\tq.ses = ses\n\tq.err = nil\n\treturn &q\n}\n"
  },
  {
    "path": "query/mql/session.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage mql\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query\"\n)\n\nconst Name = \"mql\"\n\nfunc init() {\n\tquery.RegisterLanguage(query.Language{\n\t\tName: Name,\n\t\tSession: func(qs graph.QuadStore) query.Session {\n\t\t\treturn NewSession(qs)\n\t\t},\n\t})\n}\n\ntype Session struct {\n\tqs graph.QuadStore\n}\n\nfunc NewSession(qs graph.QuadStore) *Session {\n\treturn &Session{qs: qs}\n}\n\ntype mqlIterator struct {\n\tq   *Query\n\tcol query.Collation\n\tit  iterator.Scanner\n\tres []interface{}\n}\n\nfunc (it *mqlIterator) Next(ctx context.Context) bool {\n\t// TODO: stream results\n\tif it.res != nil {\n\t\tif len(it.res) == 0 {\n\t\t\treturn false\n\t\t}\n\t\tit.res = it.res[1:]\n\t\treturn len(it.res) != 0\n\t}\n\tfor it.it.Next(ctx) {\n\t\tm := make(map[string]graph.Ref)\n\t\tit.it.TagResults(m)\n\t\tit.q.treeifyResult(m)\n\t\tfor it.it.NextPath(ctx) {\n\t\t\tm = make(map[string]graph.Ref, len(m))\n\t\t\tit.it.TagResults(m)\n\t\t\tit.q.treeifyResult(m)\n\t\t}\n\t}\n\tif err := it.it.Err(); err != nil {\n\t\treturn false\n\t}\n\tit.q.buildResults()\n\tit.res = it.q.results\n\treturn len(it.res) != 0\n}\n\nfunc (it *mqlIterator) Result() interface{} {\n\tif len(it.res) == 0 {\n\t\treturn nil\n\t}\n\treturn it.res[0]\n}\n\nfunc (it *mqlIterator) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *mqlIterator) Close() error {\n\treturn it.it.Close()\n}\n\nfunc (s *Session) Execute(ctx context.Context, input string, opt query.Options) (query.Iterator, error) {\n\tswitch opt.Collation {\n\tcase query.REPL, query.JSON:\n\tdefault:\n\t\treturn nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}\n\t}\n\tvar mqlQuery interface{}\n\tif err := json.Unmarshal([]byte(input), &mqlQuery); err != nil {\n\t\treturn nil, err\n\t}\n\tq := NewQuery(s)\n\tq.BuildIteratorTree(ctx, mqlQuery)\n\tif q.isError() {\n\t\treturn nil, q.err\n\t}\n\n\tit := q.it.Iterate()\n\tif opt.Limit > 0 {\n\t\tit = iterator.NewLimitNext(it, int64(opt.Limit))\n\t}\n\treturn &mqlIterator{\n\t\tq:   q,\n\t\tcol: opt.Collation,\n\t\tit:  it,\n\t}, nil\n}\n\nfunc (s *Session) Clear() {\n\t// Since we create a new Query underneath every query, clearing isn't necessary.\n\treturn\n}\n"
  },
  {
    "path": "query/path/morphism_apply_functions.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage path\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\n// join puts two iterators together by intersecting their result sets with an AND\n// Since we're using an and iterator, it's a good idea to put the smallest result\n// set first so that Next() produces fewer values to check Contains().\nfunc join(its ...shape.Shape) shape.Shape {\n\tif len(its) == 0 {\n\t\treturn shape.Null{}\n\t} else if _, ok := its[0].(shape.AllNodes); ok {\n\t\treturn join(its[1:]...)\n\t}\n\treturn shape.Intersect(its)\n}\n\nfunc joinOpt(main, opt shape.Shape) shape.Shape {\n\treturn shape.IntersectOptional(main, opt)\n}\n\n// isMorphism represents all nodes passed in-- if there are none, this function\n// acts as a passthrough for the previous iterator.\nfunc isMorphism(nodes ...quad.Value) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return isMorphism(nodes...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tif len(nodes) == 0 {\n\t\t\t\t// Acting as a passthrough here is equivalent to\n\t\t\t\t// building a NodesAllIterator to Next() or Contains()\n\t\t\t\t// from here as in previous versions.\n\t\t\t\treturn in, ctx\n\t\t\t}\n\t\t\ts := shape.Lookup(nodes)\n\t\t\tif _, ok := in.(shape.AllNodes); ok {\n\t\t\t\treturn s, ctx\n\t\t\t}\n\t\t\t// Anything with fixedIterators will usually have a much\n\t\t\t// smaller result set, so join isNodes first here.\n\t\t\treturn join(s, in), ctx\n\t\t},\n\t}\n}\n\n// isNodeMorphism represents all nodes passed in-- if there are none, this function\n// acts as a passthrough for the previous iterator.\nfunc isNodeMorphism(nodes ...graph.Ref) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return isNodeMorphism(nodes...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tif len(nodes) == 0 {\n\t\t\t\t// Acting as a passthrough here is equivalent to\n\t\t\t\t// building a NodesAllIterator to Next() or Contains()\n\t\t\t\t// from here as in previous versions.\n\t\t\t\treturn in, ctx\n\t\t\t}\n\t\t\t// Anything with fixedIterators will usually have a much\n\t\t\t// smaller result set, so join isNodes first here.\n\t\t\treturn join(shape.Fixed(nodes), in), ctx\n\t\t},\n\t}\n}\n\n// filterMorphism is the set of nodes that passes filters.\nfunc filterMorphism(filt []shape.ValueFilter) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return filterMorphism(filt), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.AddFilters(in, filt...), ctx\n\t\t},\n\t}\n}\n\n// hasPathMorphism is a generic form of Has morphism - it accepts a subtree that will be checked on the current path.\nfunc hasPathMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return hasPathMorphism(p), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.IntersectShapes(in, p.Shape()), ctx\n\t\t},\n\t}\n}\n\n// hasMorphism is the set of nodes that is reachable via either a *Path, a\n// single node.(string) or a list of nodes.([]string).\nfunc hasMorphism(via interface{}, rev bool, nodes ...quad.Value) morphism {\n\tvar node shape.Shape\n\tif len(nodes) == 0 {\n\t\tnode = shape.AllNodes{}\n\t} else {\n\t\tnode = shape.Lookup(nodes)\n\t}\n\treturn hasShapeMorphism(via, rev, node)\n}\n\n// hasShapeMorphism is the set of nodes that is reachable via either a *Path, a\n// single node.(string) or a list of nodes.([]string).\nfunc hasShapeMorphism(via interface{}, rev bool, nodes shape.Shape) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return hasShapeMorphism(via, rev, nodes), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.HasLabels(in, buildVia(via), nodes, ctx.labelSet, rev), ctx\n\t\t},\n\t}\n}\n\n// hasFilterMorphism is the set of nodes that is reachable via either a *Path, a\n// single node.(string) or a list of nodes.([]string) and that passes provided filters.\nfunc hasFilterMorphism(via interface{}, rev bool, filt []shape.ValueFilter) morphism {\n\treturn hasShapeMorphism(via, rev, shape.Filter{\n\t\tFrom:    shape.AllNodes{},\n\t\tFilters: filt,\n\t})\n}\n\nfunc tagMorphism(tags ...string) morphism {\n\treturn morphism{\n\t\tIsTag:    true,\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return tagMorphism(tags...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Save{From: in, Tags: tags}, ctx\n\t\t},\n\t\ttags: tags,\n\t}\n}\n\n// outMorphism iterates forward one RDF triple or via an entire path.\nfunc outMorphism(tags []string, via ...interface{}) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return inMorphism(tags, via...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Out(in, buildVia(via...), ctx.labelSet, tags...), ctx\n\t\t},\n\t\ttags: tags,\n\t}\n}\n\n// inMorphism iterates backwards one RDF triple or via an entire path.\nfunc inMorphism(tags []string, via ...interface{}) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return outMorphism(tags, via...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.In(in, buildVia(via...), ctx.labelSet, tags...), ctx\n\t\t},\n\t\ttags: tags,\n\t}\n}\n\nfunc bothMorphism(tags []string, via ...interface{}) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return bothMorphism(tags, via...), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tvia := buildVia(via...)\n\t\t\treturn shape.Union{\n\t\t\t\tshape.In(in, via, ctx.labelSet, tags...),\n\t\t\t\tshape.Out(in, via, ctx.labelSet, tags...),\n\t\t\t}, ctx\n\t\t},\n\t\ttags: tags,\n\t}\n}\n\nfunc labelContextMorphism(tags []string, via ...interface{}) morphism {\n\tvar path shape.Shape\n\tif len(via) == 0 {\n\t\tpath = nil\n\t} else {\n\t\tpath = shape.Save{From: buildVia(via...), Tags: tags}\n\t}\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) {\n\t\t\tout := ctx.copy()\n\t\t\tctx.labelSet = path\n\t\t\treturn labelContextMorphism(tags, via...), &out\n\t\t},\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tout := ctx.copy()\n\t\t\tout.labelSet = path\n\t\t\treturn in, &out\n\t\t},\n\t\ttags: tags,\n\t}\n}\n\n// labelsMorphism iterates to the uniqified set of labels from\n// the given set of nodes in the path.\nfunc labelsMorphism() morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) {\n\t\t\tpanic(\"not implemented\")\n\t\t},\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Labels(in), ctx\n\t\t},\n\t}\n}\n\n// predicatesMorphism iterates to the uniqified set of predicates from\n// the given set of nodes in the path.\nfunc predicatesMorphism(isIn bool) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) {\n\t\t\tpanic(\"not implemented: need a function from predicates to their associated edges\")\n\t\t},\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Predicates(in, isIn), ctx\n\t\t},\n\t}\n}\n\n// savePredicatesMorphism tags either forward or reverse predicates from current node\n// without affecting path.\nfunc savePredicatesMorphism(isIn bool, tag string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) {\n\t\t\treturn savePredicatesMorphism(isIn, tag), ctx\n\t\t},\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.SavePredicates(in, isIn, tag), ctx\n\t\t},\n\t}\n}\n\ntype iteratorShape struct {\n\tit   iterator.Shape\n\tsent bool\n}\n\nfunc (s *iteratorShape) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif s.sent {\n\t\treturn iterator.NewError(fmt.Errorf(\"iterator already used in query\"))\n\t}\n\tit := s.it\n\ts.it, s.sent = nil, true\n\treturn it\n}\nfunc (s *iteratorShape) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\treturn s, false\n}\n\n// iteratorMorphism simply tacks the input iterator onto the chain.\nfunc iteratorMorphism(it iterator.Shape) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return iteratorMorphism(it), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn join(&iteratorShape{it: it}, in), ctx\n\t\t},\n\t}\n}\n\n// andMorphism sticks a path onto the current iterator chain.\nfunc andMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return andMorphism(p), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn join(in, p.Shape()), ctx\n\t\t},\n\t}\n}\n\n// andOptMorphism sticks a path onto the current iterator chain and makes it optional.\nfunc andOptMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return andOptMorphism(p), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn joinOpt(in, p.Shape()), ctx\n\t\t},\n\t}\n}\n\n// orMorphism is the union, vice intersection, of a path and the current iterator.\nfunc orMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return orMorphism(p), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Union{in, p.Shape()}, ctx\n\t\t},\n\t}\n}\n\nfunc followMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return followMorphism(p.Reverse()), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn p.ShapeFrom(in), ctx\n\t\t},\n\t}\n}\n\ntype iteratorBuilder func(qs graph.QuadStore) iterator.Shape\n\nfunc (s iteratorBuilder) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\treturn s(qs)\n}\nfunc (s iteratorBuilder) Optimize(ctx context.Context, r shape.Optimizer) (shape.Shape, bool) {\n\treturn s, false\n}\n\nfunc followRecursiveMorphism(p *Path, maxDepth int, depthTags []string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) {\n\t\t\treturn followRecursiveMorphism(p.Reverse(), maxDepth, depthTags), ctx\n\t\t},\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn iteratorBuilder(func(qs graph.QuadStore) iterator.Shape {\n\t\t\t\tin := in.BuildIterator(qs)\n\t\t\t\tit := iterator.NewRecursive(in, p.MorphismFor(qs), maxDepth)\n\t\t\t\tfor _, s := range depthTags {\n\t\t\t\t\tit.AddDepthTag(s)\n\t\t\t\t}\n\t\t\t\treturn it\n\t\t\t}), ctx\n\t\t},\n\t}\n}\n\n// exceptMorphism removes all results on p.(*Path) from the current iterators.\nfunc exceptMorphism(p *Path) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return exceptMorphism(p), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn join(in, shape.Except{From: shape.AllNodes{}, Exclude: p.Shape()}), ctx\n\t\t},\n\t}\n}\n\n// uniqueMorphism removes duplicate values from current path.\nfunc uniqueMorphism() morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return uniqueMorphism(), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Unique{From: in}, ctx\n\t\t},\n\t}\n}\n\nfunc saveMorphism(via interface{}, tag string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return saveMorphism(via, tag), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.SaveViaLabels(in, buildVia(via), ctx.labelSet, tag, false, false), ctx\n\t\t},\n\t\ttags: []string{tag},\n\t}\n}\n\nfunc saveReverseMorphism(via interface{}, tag string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return saveReverseMorphism(via, tag), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.SaveViaLabels(in, buildVia(via), ctx.labelSet, tag, true, false), ctx\n\t\t},\n\t\ttags: []string{tag},\n\t}\n}\n\nfunc saveOptionalMorphism(via interface{}, tag string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return saveOptionalMorphism(via, tag), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.SaveViaLabels(in, buildVia(via), ctx.labelSet, tag, false, true), ctx\n\t\t},\n\t\ttags: []string{tag},\n\t}\n}\n\nfunc saveOptionalReverseMorphism(via interface{}, tag string) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return saveOptionalReverseMorphism(via, tag), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.SaveViaLabels(in, buildVia(via), ctx.labelSet, tag, true, true), ctx\n\t\t},\n\t\ttags: []string{tag},\n\t}\n}\n\nfunc buildVia(via ...interface{}) shape.Shape {\n\tif len(via) == 0 {\n\t\treturn shape.AllNodes{}\n\t} else if len(via) == 1 {\n\t\tv := via[0]\n\t\tswitch p := v.(type) {\n\t\tcase nil:\n\t\t\treturn shape.AllNodes{}\n\t\tcase *Path:\n\t\t\treturn p.Shape()\n\t\tcase quad.Value:\n\t\t\treturn shape.Lookup{p}\n\t\tcase []quad.Value:\n\t\t\treturn shape.Lookup(p)\n\t\t}\n\t}\n\tnodes := make([]quad.Value, 0, len(via))\n\tfor _, v := range via {\n\t\tqv, ok := quad.AsValue(v)\n\t\tif !ok {\n\t\t\tpanic(fmt.Errorf(\"Invalid type passed to buildViaPath: %v (%T)\", v, v))\n\t\t}\n\t\tnodes = append(nodes, qv)\n\t}\n\treturn shape.Lookup(nodes)\n}\n\n// skipMorphism will skip a number of values-- if there are none, this function\n// acts as a passthrough for the previous iterator.\nfunc skipMorphism(v int64) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return skipMorphism(v), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tif v == 0 {\n\t\t\t\t// Acting as a passthrough\n\t\t\t\treturn in, ctx\n\t\t\t}\n\t\t\treturn shape.Page{From: in, Skip: v}, ctx\n\t\t},\n\t}\n}\n\nfunc orderMorphism() morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return orderMorphism(), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Sort{From: in}, ctx\n\t\t},\n\t}\n}\n\n// limitMorphism will limit a number of values-- if number is negative or zero, this function\n// acts as a passthrough for the previous iterator.\nfunc limitMorphism(v int64) morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return limitMorphism(v), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\tif v <= 0 {\n\t\t\t\t// Acting as a passthrough\n\t\t\t\treturn in, ctx\n\t\t\t}\n\t\t\treturn shape.Page{From: in, Limit: v}, ctx\n\t\t},\n\t}\n}\n\n// countMorphism will return count of values.\nfunc countMorphism() morphism {\n\treturn morphism{\n\t\tReversal: func(ctx *pathContext) (morphism, *pathContext) { return countMorphism(), ctx },\n\t\tApply: func(in shape.Shape, ctx *pathContext) (shape.Shape, *pathContext) {\n\t\t\treturn shape.Count{Values: in}, ctx\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "query/path/path.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage path\n\nimport (\n\t\"context\"\n\t\"regexp\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype applyMorphism func(shape.Shape, *pathContext) (shape.Shape, *pathContext)\n\ntype morphism struct {\n\tIsTag    bool\n\tReversal func(*pathContext) (morphism, *pathContext)\n\tApply    applyMorphism\n\ttags     []string\n}\n\n// pathContext allows a high-level change to the way paths are constructed. Some\n// functions may change the context, causing following chained calls to act\n// differently.\n//\n// In a sense, this is a global state which can be changed as the path\n// continues. And as with dealing with any global state, care should be taken:\n//\n// When modifying the context in Apply(), please copy the passed struct,\n// modifying the relevant fields if need be (or pass the given context onward).\n//\n// Under Reversal(), any functions that wish to change the context should\n// appropriately change the passed context (that is, the context that came after\n// them will now be what the application of the function would have been) and\n// then yield a pointer to their own member context as the return value.\n//\n// For more examples, look at the morphisms which claim the individual fields.\ntype pathContext struct {\n\t// TODO(dennwc): replace with net/context?\n\n\t// Represents the path to the limiting set of labels that should be considered under traversal.\n\t// inMorphism, outMorphism, et al should constrain edges by this set.\n\t// A nil in this field represents all labels.\n\t//\n\t// Claimed by the withLabel morphism\n\tlabelSet shape.Shape\n}\n\nfunc (c pathContext) copy() pathContext {\n\treturn pathContext{\n\t\tlabelSet: c.labelSet,\n\t}\n}\n\n// Path represents either a morphism (a pre-defined path stored for later use),\n// or a concrete path, consisting of a morphism and an underlying QuadStore.\ntype Path struct {\n\tstack       []morphism\n\tqs          graph.QuadStore // Optionally. A nil qs is equivalent to a morphism.\n\tbaseContext pathContext\n}\n\n// IsMorphism returns whether this Path is a morphism.\nfunc (p *Path) IsMorphism() bool { return p.qs == nil }\n\n// StartMorphism creates a new Path with no underlying QuadStore.\nfunc StartMorphism(nodes ...quad.Value) *Path {\n\treturn StartPath(nil, nodes...)\n}\n\nfunc newPath(qs graph.QuadStore, m ...morphism) *Path {\n\tqs = graph.Unwrap(qs)\n\treturn &Path{\n\t\tstack: m,\n\t\tqs:    qs,\n\t}\n}\n\n// StartPath creates a new Path from a set of nodes and an underlying QuadStore.\nfunc StartPath(qs graph.QuadStore, nodes ...quad.Value) *Path {\n\treturn newPath(qs, isMorphism(nodes...))\n}\n\n// StartPathNodes creates a new Path from a set of nodes and an underlying QuadStore.\nfunc StartPathNodes(qs graph.QuadStore, nodes ...graph.Ref) *Path {\n\treturn newPath(qs, isNodeMorphism(nodes...))\n}\n\n// PathFromIterator creates a new Path from a set of nodes contained in iterator.\nfunc PathFromIterator(qs graph.QuadStore, it iterator.Shape) *Path {\n\treturn newPath(qs, iteratorMorphism(it))\n}\n\n// NewPath creates a new, empty Path.\nfunc NewPath(qs graph.QuadStore) *Path {\n\treturn newPath(qs)\n}\n\n// Clone returns a clone of the current path.\nfunc (p *Path) Clone() *Path {\n\tstack := p.stack\n\treturn &Path{\n\t\tstack:       stack[:len(stack):len(stack)],\n\t\tqs:          p.qs,\n\t\tbaseContext: p.baseContext,\n\t}\n}\n\n// Unexported clone method returns a *Path with a copy of the original stack,\n// with assumption that the new stack will be appended to.\nfunc (p *Path) clone() *Path {\n\tstack := p.stack\n\tp.stack = stack[:len(stack):len(stack)]\n\treturn &Path{\n\t\tstack:       stack,\n\t\tqs:          p.qs,\n\t\tbaseContext: p.baseContext,\n\t}\n}\n\n// Reverse returns a new Path that is the reverse of the current one.\nfunc (p *Path) Reverse() *Path {\n\tnewPath := NewPath(p.qs)\n\tctx := &newPath.baseContext\n\tfor i := len(p.stack) - 1; i >= 0; i-- {\n\t\tvar revMorphism morphism\n\t\trevMorphism, ctx = p.stack[i].Reversal(ctx)\n\t\tnewPath.stack = append(newPath.stack, revMorphism)\n\t}\n\treturn newPath\n}\n\n// Is declares that the current nodes in this path are only the nodes\n// passed as arguments.\nfunc (p *Path) Is(nodes ...quad.Value) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, isMorphism(nodes...))\n\treturn np\n}\n\n// Regex represents the nodes that are matching provided regexp pattern.\n// It will only include Raw and String values.\nfunc (p *Path) Regex(pattern *regexp.Regexp) *Path {\n\treturn p.Filters(shape.Regexp{Re: pattern, Refs: false})\n}\n\n// RegexWithRefs is the same as Regex, but also matches IRIs and BNodes.\n//\n// Consider using it carefully. In most cases it's better to reconsider\n// your graph structure instead of relying on slow unoptimizable regexp.\n//\n// An example of incorrect usage is to match IRIs:\n// \t<http://example.org/page>\n// \t<http://example.org/page/foo>\n// Via regexp like:\n//\thttp://example.org/page.*\n//\n// The right way is to explicitly link graph nodes and query them by this relation:\n// \t<http://example.org/page/foo> <type> <http://example.org/page>\nfunc (p *Path) RegexWithRefs(pattern *regexp.Regexp) *Path {\n\treturn p.Filters(shape.Regexp{Re: pattern, Refs: true})\n}\n\n// Filter represents the nodes that are passing comparison with provided value.\nfunc (p *Path) Filter(op iterator.Operator, node quad.Value) *Path {\n\treturn p.Filters(shape.Comparison{Op: op, Val: node})\n}\n\n// Filters represents the nodes that are passing provided filters.\nfunc (p *Path) Filters(filters ...shape.ValueFilter) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, filterMorphism(filters))\n\treturn np\n}\n\n// Tag adds tag strings to the nodes at this point in the path for each result\n// path in the set.\nfunc (p *Path) Tag(tags ...string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, tagMorphism(tags...))\n\treturn np\n}\n\n// Out updates this Path to represent the nodes that are adjacent to the\n// current nodes, via the given outbound predicate.\n//\n// For example:\n//  // Returns the list of nodes that \"B\" follows.\n//  //\n//  // Will return []string{\"F\"} if there is a predicate (edge) from \"B\"\n//  // to \"F\" labelled \"follows\".\n//  StartPath(qs, \"A\").Out(\"follows\")\nfunc (p *Path) Out(via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, outMorphism(nil, via...))\n\treturn np\n}\n\n// In updates this Path to represent the nodes that are adjacent to the\n// current nodes, via the given inbound predicate.\n//\n// For example:\n//  // Return the list of nodes that follow \"B\".\n//  //\n//  // Will return []string{\"A\", \"C\", \"D\"} if there are the appropriate\n//  // edges from those nodes to \"B\" labelled \"follows\".\n//  StartPath(qs, \"B\").In(\"follows\")\nfunc (p *Path) In(via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, inMorphism(nil, via...))\n\treturn np\n}\n\n// InWithTags is exactly like In, except it tags the value of the predicate\n// traversed with the tags provided.\nfunc (p *Path) InWithTags(tags []string, via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, inMorphism(tags, via...))\n\treturn np\n}\n\n// OutWithTags is exactly like In, except it tags the value of the predicate\n// traversed with the tags provided.\nfunc (p *Path) OutWithTags(tags []string, via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, outMorphism(tags, via...))\n\treturn np\n}\n\n// Both updates this path following both inbound and outbound predicates.\n//\n// For example:\n//  // Return the list of nodes that follow or are followed by \"B\".\n//  //\n//  // Will return []string{\"A\", \"C\", \"D\", \"F} if there are the appropriate\n//  // edges from those nodes to \"B\" labelled \"follows\", in either direction.\n//  StartPath(qs, \"B\").Both(\"follows\")\nfunc (p *Path) Both(via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, bothMorphism(nil, via...))\n\treturn np\n}\n\n// BothWithTags is exactly like Both, except it tags the value of the predicate\n// traversed with the tags provided.\nfunc (p *Path) BothWithTags(tags []string, via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, bothMorphism(tags, via...))\n\treturn np\n}\n\n// Labels updates this path to represent the nodes of the labels\n// of inbound and outbound quads.\nfunc (p *Path) Labels() *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, labelsMorphism())\n\treturn np\n}\n\n// InPredicates updates this path to represent the nodes of the valid inbound\n// predicates from the current nodes.\n//\n// For example:\n//  // Returns a list of predicates valid from \"bob\"\n//  //\n//  // Will return []string{\"follows\"} if there are any things that \"follow\" Bob\n//  StartPath(qs, \"bob\").InPredicates()\nfunc (p *Path) InPredicates() *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, predicatesMorphism(true))\n\treturn np\n}\n\n// OutPredicates updates this path to represent the nodes of the valid inbound\n// predicates from the current nodes.\n//\n// For example:\n//  // Returns a list of predicates valid from \"bob\"\n//  //\n//  // Will return []string{\"follows\", \"status\"} if there are edges from \"bob\"\n//  // labelled \"follows\", and edges from \"bob\" that describe his \"status\".\n//  StartPath(qs, \"bob\").OutPredicates()\nfunc (p *Path) OutPredicates() *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, predicatesMorphism(false))\n\treturn np\n}\n\n// SavePredicates saves either forward or reverse predicates of current node\n// without changing path location.\nfunc (p *Path) SavePredicates(rev bool, tag string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, savePredicatesMorphism(rev, tag))\n\treturn np\n}\n\n// And updates the current Path to represent the nodes that match both the\n// current Path so far, and the given Path.\nfunc (p *Path) And(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, andMorphism(path))\n\treturn np\n}\n\n// Optional adds an optional path to evaluate. This path will only contribute to tags and won't change iteration results.\nfunc (p *Path) Optional(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, andOptMorphism(path.Reverse()))\n\treturn np\n}\n\n// Or updates the current Path to represent the nodes that match either the\n// current Path so far, or the given Path.\nfunc (p *Path) Or(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, orMorphism(path))\n\treturn np\n}\n\n// Except updates the current Path to represent the all of the current nodes\n// except those in the supplied Path.\n//\n// For example:\n//  // Will return []string{\"B\"}\n//  StartPath(qs, \"A\", \"B\").Except(StartPath(qs, \"A\"))\nfunc (p *Path) Except(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, exceptMorphism(path))\n\treturn np\n}\n\n// Unique updates the current Path to contain only unique nodes.\nfunc (p *Path) Unique() *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, uniqueMorphism())\n\treturn np\n}\n\n// Follow allows you to stitch two paths together. The resulting path will start\n// from where the first path left off and continue iterating down the path given.\nfunc (p *Path) Follow(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, followMorphism(path))\n\treturn np\n}\n\n// FollowReverse is the same as follow, except it will iterate backwards up the\n// path given as argument.\nfunc (p *Path) FollowReverse(path *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, followMorphism(path.Reverse()))\n\treturn np\n}\n\n// FollowRecursive will repeatedly follow the given string predicate or Path\n// object starting from the given node(s), through the morphism or pattern\n// provided, ignoring loops. For example, this turns \"parent\" into \"all\n// ancestors\", by repeatedly following the \"parent\" connection on the result of\n// the parent nodes.\n//\n// The second argument, \"maxDepth\" is the maximum number of recursive steps before\n// stopping and returning.\n// If -1 is passed, it will have no limit.\n// If 0 is passed, it will use the default value of 50 steps before returning.\n// If 1 is passed, it will stop after 1 step before returning, and so on.\n//\n// The third argument, \"depthTags\" is a set of tags that will return strings of\n// numeric values relating to how many applications of the path were applied the\n// first time the result node was seen.\n//\n// This is a very expensive operation in practice. Be sure to use it wisely.\nfunc (p *Path) FollowRecursive(via interface{}, maxDepth int, depthTags []string) *Path {\n\tvar path *Path\n\tswitch v := via.(type) {\n\tcase string:\n\t\tpath = StartMorphism().Out(v)\n\tcase quad.Value:\n\t\tpath = StartMorphism().Out(v)\n\tcase *Path:\n\t\tpath = v\n\tdefault:\n\t\tpanic(\"did not pass a string predicate or a Path to FollowRecursive\")\n\t}\n\tnp := p.clone()\n\tnp.stack = append(p.stack, followRecursiveMorphism(path, maxDepth, depthTags))\n\treturn np\n}\n\n// Save will, from the current nodes in the path, retrieve the node\n// one linkage away (given by either a path or a predicate), add the given\n// tag, and propagate that to the result set.\n//\n// For example:\n//  // Will return []map[string]string{{\"social_status: \"cool\"}}\n//  StartPath(qs, \"B\").Save(\"status\", \"social_status\"\nfunc (p *Path) Save(via interface{}, tag string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, saveMorphism(via, tag))\n\treturn np\n}\n\n// SaveReverse is the same as Save, only in the reverse direction\n// (the subject of the linkage should be tagged, instead of the object).\nfunc (p *Path) SaveReverse(via interface{}, tag string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, saveReverseMorphism(via, tag))\n\treturn np\n}\n\n// SaveOptional is the same as Save, but does not require linkage to exist.\nfunc (p *Path) SaveOptional(via interface{}, tag string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, saveOptionalMorphism(via, tag))\n\treturn np\n}\n\n// SaveOptionalReverse is the same as SaveReverse, but does not require linkage to exist.\nfunc (p *Path) SaveOptionalReverse(via interface{}, tag string) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, saveOptionalReverseMorphism(via, tag))\n\treturn np\n}\n\n// HasPath limits the paths to be ones where the current nodes have a given subpath.\nfunc (p *Path) HasPath(p2 *Path) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, hasPathMorphism(p2.Reverse()))\n\treturn np\n}\n\n// Has limits the paths to be ones where the current nodes have some linkage\n// to some known node.\nfunc (p *Path) Has(via interface{}, nodes ...quad.Value) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, hasMorphism(via, false, nodes...))\n\treturn np\n}\n\n// HasReverse limits the paths to be ones where some known node have some linkage\n// to the current nodes.\nfunc (p *Path) HasReverse(via interface{}, nodes ...quad.Value) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, hasMorphism(via, true, nodes...))\n\treturn np\n}\n\n// HasFilter limits the paths to be ones where the current nodes have some linkage\n// to some nodes that pass provided filters.\nfunc (p *Path) HasFilter(via interface{}, rev bool, filt ...shape.ValueFilter) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, hasFilterMorphism(via, rev, filt))\n\treturn np\n}\n\n// LabelContext restricts the following operations (such as In, Out) to only\n// traverse edges that match the given set of labels.\nfunc (p *Path) LabelContext(via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, labelContextMorphism(nil, via...))\n\treturn np\n}\n\n// LabelContextWithTags is exactly like LabelContext, except it tags the value\n// of the label used in the traversal with the tags provided.\nfunc (p *Path) LabelContextWithTags(tags []string, via ...interface{}) *Path {\n\tnp := p.clone()\n\tnp.stack = append(np.stack, labelContextMorphism(tags, via...))\n\treturn np\n}\n\n// Back returns to a previously tagged place in the path. Any constraints applied after the Tag will remain in effect, but traversal continues from the tagged point instead, not from the end of the chain.\n//\n// For example:\n//  // Will return \"bob\" iff \"bob\" is cool\n//  StartPath(qs, \"bob\").Tag(\"person_tag\").Out(\"status\").Is(\"cool\").Back(\"person_tag\")\nfunc (p *Path) Back(tag string) *Path {\n\tnewPath := NewPath(p.qs)\n\ti := len(p.stack) - 1\n\tctx := &newPath.baseContext\n\tfor {\n\t\tif i < 0 {\n\t\t\treturn p.Reverse()\n\t\t}\n\t\tif p.stack[i].IsTag {\n\t\t\tfor _, x := range p.stack[i].tags {\n\t\t\t\tif x == tag {\n\t\t\t\t\t// Found what we're looking for.\n\t\t\t\t\tp.stack = p.stack[:i+1]\n\t\t\t\t\treturn p.And(newPath)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvar revMorphism morphism\n\t\trevMorphism, ctx = p.stack[i].Reversal(ctx)\n\t\tnewPath.stack = append(newPath.stack, revMorphism)\n\t\ti--\n\t}\n}\n\n// BuildIterator returns an iterator from this given Path.  Note that you must\n// call this with a full path (not a morphism), since a morphism does not have\n// the ability to fetch the underlying quads.  This function will panic if\n// called with a morphism (i.e. if p.IsMorphism() is true).\nfunc (p *Path) BuildIterator(ctx context.Context) iterator.Shape {\n\tif p.IsMorphism() {\n\t\tpanic(\"Building an iterator from a morphism. Bind a QuadStore with BuildIteratorOn(qs)\")\n\t}\n\treturn p.BuildIteratorOn(ctx, p.qs)\n}\n\n// BuildIteratorOn will return an iterator for this path on the given QuadStore.\nfunc (p *Path) BuildIteratorOn(ctx context.Context, qs graph.QuadStore) iterator.Shape {\n\treturn shape.BuildIterator(ctx, qs, p.Shape())\n}\n\n// MorphismFor returns the morphism of this path. The returned value is a\n// function that, when given an existing Iterator, will return a new Iterator\n// that yields the subset of values from the existing iterator matched by the\n// current Path.\nfunc (p *Path) MorphismFor(qs graph.QuadStore) iterator.Morphism {\n\treturn func(it iterator.Shape) iterator.Shape {\n\t\treturn p.ShapeFrom(&iteratorShape{it: it}).BuildIterator(qs)\n\t}\n}\n\n// Skip will omit a number of values from result set.\nfunc (p *Path) Skip(v int64) *Path {\n\tp.stack = append(p.stack, skipMorphism(v))\n\treturn p\n}\n\nfunc (p *Path) Order() *Path {\n\tp.stack = append(p.stack, orderMorphism())\n\treturn p\n}\n\n// Limit will limit a number of values in result set.\nfunc (p *Path) Limit(v int64) *Path {\n\tp.stack = append(p.stack, limitMorphism(v))\n\treturn p\n}\n\n// Count will count a number of results as it's own result set.\nfunc (p *Path) Count() *Path {\n\tp.stack = append(p.stack, countMorphism())\n\treturn p\n}\n\n// Iterate is an shortcut for graph.Iterate.\nfunc (p *Path) Iterate(ctx context.Context) *iterator.Chain {\n\treturn shape.Iterate(ctx, p.qs, p.Shape())\n}\nfunc (p *Path) Shape() shape.Shape {\n\treturn p.ShapeFrom(shape.AllNodes{})\n}\nfunc (p *Path) ShapeFrom(from shape.Shape) shape.Shape {\n\ts := from\n\tctx := &p.baseContext\n\tfor _, m := range p.stack {\n\t\ts, ctx = m.Apply(s, ctx)\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "query/path/path_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage path_test\n\nimport (\n\t\"testing\"\n\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/query/path/pathtest\"\n)\n\nfunc TestMorphisms(t *testing.T) {\n\tpathtest.RunTestMorphisms(t, nil)\n}\n"
  },
  {
    "path": "query/path/pathtest/pathtest.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage pathtest\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n)\n\n// This is a simple test graph.\n//\n//  +-------+                        +------+\n//  | alice |-----                 ->| fred |<--\n//  +-------+     \\---->+-------+-/  +------+   \\-+-------+\n//                ----->| #bob# |       |         | emily |\n//  +---------+--/  --->+-------+       |         +-------+\n//  | charlie |    /                    v\n//  +---------+   /                  +--------+\n//    \\---    +--------+             | #greg# |\n//        \\-->| #dani# |------------>+--------+\n//            +--------+\n\nfunc makeTestStore(t testing.TB, fnc testutil.DatabaseFunc, quads ...quad.Quad) graph.QuadStore {\n\tif len(quads) == 0 {\n\t\tquads = testutil.LoadGraph(t, \"data/testdata.nq\")\n\t}\n\tvar (\n\t\tqs   graph.QuadStore\n\t\topts graph.Options\n\t)\n\tif fnc != nil {\n\t\tqs, opts = fnc(t)\n\t} else {\n\t\tqs, _ = graph.NewQuadStore(\"memstore\", \"\", nil)\n\t}\n\t_ = testutil.MakeWriter(t, qs, opts, quads...)\n\treturn qs\n}\n\nfunc runTopLevel(qs graph.QuadStore, path *path.Path, opt bool) ([]quad.Value, error) {\n\tpb := path.Iterate(context.TODO())\n\tif !opt {\n\t\tpb = pb.UnOptimized()\n\t}\n\treturn pb.Paths(false).AllValues(qs)\n}\n\nfunc runTag(qs graph.QuadStore, path *path.Path, tag string, opt, keepEmpty bool) ([]quad.Value, error) {\n\tvar out []quad.Value\n\tpb := path.Iterate(context.TODO())\n\tif !opt {\n\t\tpb = pb.UnOptimized()\n\t}\n\terr := pb.Paths(true).TagEach(func(tags map[string]graph.Ref) error {\n\t\tif t, ok := tags[tag]; ok {\n\t\t\ttv, err := qs.NameOf(t)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tout = append(out, tv)\n\t\t} else if keepEmpty {\n\t\t\tout = append(out, vEmpty)\n\t\t}\n\t\treturn nil\n\t})\n\treturn out, err\n}\n\nfunc runAllTags(qs graph.QuadStore, path *path.Path, opt bool) ([]map[string]quad.Value, error) {\n\tvar out []map[string]quad.Value\n\tpb := path.Iterate(context.TODO())\n\tif !opt {\n\t\tpb = pb.UnOptimized()\n\t}\n\terr := pb.Paths(true).TagValues(qs, func(tags map[string]quad.Value) error {\n\t\tout = append(out, tags)\n\t\treturn nil\n\t})\n\treturn out, err\n}\n\ntype test struct {\n\tskip      bool\n\tmessage   string\n\tpath      *path.Path\n\texpect    []quad.Value\n\texpectAlt [][]quad.Value\n\ttag       string\n\tunsorted  bool\n\tempty     bool // do not skip empty tags\n}\n\n// Define morphisms without a QuadStore\n\nconst (\n\tvEmpty = quad.String(\"\")\n\n\tvFollows   = quad.IRI(\"follows\")\n\tvAre       = quad.IRI(\"are\")\n\tvStatus    = quad.IRI(\"status\")\n\tvPredicate = quad.IRI(\"predicates\")\n\n\tvCool       = quad.String(\"cool_person\")\n\tvSmart      = quad.String(\"smart_person\")\n\tvSmartGraph = quad.IRI(\"smart_graph\")\n\n\tvAlice   = quad.IRI(\"alice\")\n\tvBob     = quad.IRI(\"bob\")\n\tvCharlie = quad.IRI(\"charlie\")\n\tvDani    = quad.IRI(\"dani\")\n\tvFred    = quad.IRI(\"fred\")\n\tvGreg    = quad.IRI(\"greg\")\n\tvEmily   = quad.IRI(\"emily\")\n)\n\nvar (\n\tgrandfollows = path.StartMorphism().Out(vFollows).Out(vFollows)\n)\n\nfunc testSet(qs graph.QuadStore) []test {\n\treturn []test{\n\t\t{\n\t\t\tmessage: \"out\",\n\t\t\tpath:    path.StartPath(qs, vAlice).Out(vFollows),\n\t\t\texpect:  []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"out (any)\",\n\t\t\tpath:    path.StartPath(qs, vBob).Out(),\n\t\t\texpect:  []quad.Value{vFred, vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \"out (raw)\",\n\t\t\tpath:    path.StartPath(qs, quad.Raw(vAlice.String())).Out(quad.Raw(vFollows.String())),\n\t\t\texpect:  []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"in\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(vFollows),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"in (any)\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"filter nodes\",\n\t\t\tpath:    path.StartPath(qs).Filter(iterator.CompareGT, quad.IRI(\"p\")),\n\t\t\texpect:  []quad.Value{vPredicate, vSmartGraph, vStatus},\n\t\t},\n\t\t{\n\t\t\tmessage: \"in with filter\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(vFollows).Filter(iterator.CompareGT, quad.IRI(\"c\")),\n\t\t\texpect:  []quad.Value{vCharlie, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"in with regex\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(vFollows).Regex(regexp.MustCompile(\"ar?li.*e\")),\n\t\t\texpect:  nil,\n\t\t},\n\t\t{\n\t\t\tmessage: \"in with regex (include IRIs)\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(vFollows).RegexWithRefs(regexp.MustCompile(\"ar?li.*e\")),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie},\n\t\t},\n\t\t{\n\t\t\tmessage: \"path Out\",\n\t\t\tpath:    path.StartPath(qs, vBob).Out(path.StartPath(qs, vPredicate).Out(vAre)),\n\t\t\texpect:  []quad.Value{vFred, vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \"path Out (raw)\",\n\t\t\tpath:    path.StartPath(qs, quad.Raw(vBob.String())).Out(path.StartPath(qs, quad.Raw(vPredicate.String())).Out(quad.Raw(vAre.String()))),\n\t\t\texpect:  []quad.Value{vFred, vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \"And\",\n\t\t\tpath: path.StartPath(qs, vDani).Out(vFollows).And(\n\t\t\t\tpath.StartPath(qs, vCharlie).Out(vFollows)),\n\t\t\texpect: []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Or\",\n\t\t\tpath: path.StartPath(qs, vFred).Out(vFollows).Or(\n\t\t\t\tpath.StartPath(qs, vAlice).Out(vFollows)),\n\t\t\texpect: []quad.Value{vBob, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage: \"implicit All\",\n\t\t\tpath:    path.StartPath(qs),\n\t\t\texpect:  []quad.Value{vAlice, vBob, vCharlie, vDani, vEmily, vFred, vGreg, vFollows, vStatus, vCool, vPredicate, vAre, vSmartGraph, vSmart},\n\t\t},\n\t\t{\n\t\t\tmessage: \"follow\",\n\t\t\tpath:    path.StartPath(qs, vCharlie).Follow(path.StartMorphism().Out(vFollows).Out(vFollows)),\n\t\t\texpect:  []quad.Value{vBob, vFred, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage: \"followR\",\n\t\t\tpath:    path.StartPath(qs, vFred).FollowReverse(path.StartMorphism().Out(vFollows).Out(vFollows)),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"is, tag, instead of FollowR\",\n\t\t\tpath:    path.StartPath(qs).Tag(\"first\").Follow(path.StartMorphism().Out(vFollows).Out(vFollows)).Is(vFred),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie, vDani},\n\t\t\ttag:     \"first\",\n\t\t},\n\t\t{\n\t\t\tmessage: \"Except to filter out a single vertex\",\n\t\t\tpath:    path.StartPath(qs, vAlice, vBob).Except(path.StartPath(qs, vAlice)),\n\t\t\texpect:  []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"chained Except\",\n\t\t\tpath:    path.StartPath(qs, vAlice, vBob, vCharlie).Except(path.StartPath(qs, vBob)).Except(path.StartPath(qs, vAlice)),\n\t\t\texpect:  []quad.Value{vCharlie},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Unique\",\n\t\t\tpath:    path.StartPath(qs, vAlice, vBob, vCharlie).Out(vFollows).Unique(),\n\t\t\texpect:  []quad.Value{vBob, vDani, vFred},\n\t\t},\n\t\t{\n\t\t\tmessage: \"simple save\",\n\t\t\tpath:    path.StartPath(qs).Save(vStatus, \"somecool\"),\n\t\t\ttag:     \"somecool\",\n\t\t\texpect:  []quad.Value{vCool, vCool, vCool, vSmart, vSmart},\n\t\t},\n\t\t{\n\t\t\tmessage: \"simple saveR\",\n\t\t\tpath:    path.StartPath(qs, vCool).SaveReverse(vStatus, \"who\"),\n\t\t\ttag:     \"who\",\n\t\t\texpect:  []quad.Value{vGreg, vDani, vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"save with a next path\",\n\t\t\tpath:    path.StartPath(qs, vDani, vBob).Save(vFollows, \"target\"),\n\t\t\ttag:     \"target\",\n\t\t\texpect:  []quad.Value{vBob, vFred, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage: \"save all with a next path\",\n\t\t\tpath:    path.StartPath(qs).Save(vFollows, \"target\"),\n\t\t\ttag:     \"target\",\n\t\t\texpect:  []quad.Value{vBob, vBob, vBob, vDani, vFred, vFred, vGreg, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage: \"simple Has\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus, vCool),\n\t\t\texpect:  []quad.Value{vGreg, vDani, vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"filter nodes with has\",\n\t\t\tpath: path.StartPath(qs).HasFilter(vFollows, false, shape.Comparison{\n\t\t\t\tOp: iterator.CompareGT, Val: quad.IRI(\"f\"),\n\t\t\t}),\n\t\t\texpect: []quad.Value{vBob, vDani, vEmily, vFred},\n\t\t},\n\t\t{\n\t\t\tmessage: \"has path\",\n\t\t\tpath:    path.StartPath(qs).HasPath(path.StartMorphism().Out(vStatus).Is(vCool)),\n\t\t\texpect:  []quad.Value{vGreg, vDani, vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"string prefix\",\n\t\t\tpath: path.StartPath(qs).Filters(shape.Wildcard{\n\t\t\t\tPattern: `bo%`,\n\t\t\t}),\n\t\t\texpect: []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"three letters and range\",\n\t\t\tpath: path.StartPath(qs).Filters(shape.Wildcard{\n\t\t\t\tPattern: `???`,\n\t\t\t}, shape.Comparison{\n\t\t\t\tOp: iterator.CompareGT, Val: quad.IRI(\"b\"),\n\t\t\t}),\n\t\t\texpect: []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"part in string\",\n\t\t\tpath: path.StartPath(qs).Filters(shape.Wildcard{\n\t\t\t\tPattern: `%ed%`,\n\t\t\t}),\n\t\t\texpect: []quad.Value{vFred, vPredicate},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Limit\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus, vCool).Limit(2),\n\t\t\t// TODO(dennwc): resolve this ordering issue\n\t\t\texpectAlt: [][]quad.Value{\n\t\t\t\t{vBob, vGreg},\n\t\t\t\t{vBob, vDani},\n\t\t\t\t{vDani, vGreg},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Skip\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus, vCool).Skip(2),\n\t\t\texpectAlt: [][]quad.Value{\n\t\t\t\t{vBob},\n\t\t\t\t{vDani},\n\t\t\t\t{vGreg},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Skip and Limit\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus, vCool).Skip(1).Limit(1),\n\t\t\texpectAlt: [][]quad.Value{\n\t\t\t\t{vBob},\n\t\t\t\t{vDani},\n\t\t\t\t{vGreg},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Count\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus).Count(),\n\t\t\texpect:  []quad.Value{quad.Int(5)},\n\t\t},\n\t\t{\n\t\t\tmessage: \"double Has\",\n\t\t\tpath:    path.StartPath(qs).Has(vStatus, vCool).Has(vFollows, vFred),\n\t\t\texpect:  []quad.Value{vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"simple HasReverse\",\n\t\t\tpath:    path.StartPath(qs).HasReverse(vStatus, vBob),\n\t\t\texpect:  []quad.Value{vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \".Tag()-.Is()-.Back()\",\n\t\t\tpath:    path.StartPath(qs, vBob).In(vFollows).Tag(\"foo\").Out(vStatus).Is(vCool).Back(\"foo\"),\n\t\t\texpect:  []quad.Value{vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"do multiple .Back()s\",\n\t\t\tpath:    path.StartPath(qs, vEmily).Out(vFollows).Tag(\"f\").Out(vFollows).Out(vStatus).Is(vCool).Back(\"f\").In(vFollows).In(vFollows).Tag(\"acd\").Out(vStatus).Is(vCool).Back(\"f\"),\n\t\t\ttag:     \"acd\",\n\t\t\texpect:  []quad.Value{vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"Labels()\",\n\t\t\tpath:    path.StartPath(qs, vGreg).Labels(),\n\t\t\texpect:  []quad.Value{vSmartGraph},\n\t\t},\n\t\t{\n\t\t\tmessage: \"InPredicates()\",\n\t\t\tpath:    path.StartPath(qs, vBob).InPredicates(),\n\t\t\texpect:  []quad.Value{vFollows},\n\t\t},\n\t\t{\n\t\t\tmessage: \"OutPredicates()\",\n\t\t\tpath:    path.StartPath(qs, vBob).OutPredicates(),\n\t\t\texpect:  []quad.Value{vFollows, vStatus},\n\t\t},\n\t\t{\n\t\t\tmessage: \"SavePredicates(in)\",\n\t\t\tpath:    path.StartPath(qs, vBob).SavePredicates(true, \"pred\"),\n\t\t\texpect:  []quad.Value{vFollows, vFollows, vFollows},\n\t\t\ttag:     \"pred\",\n\t\t},\n\t\t{\n\t\t\tmessage: \"SavePredicates(out)\",\n\t\t\tpath:    path.StartPath(qs, vBob).SavePredicates(false, \"pred\"),\n\t\t\texpect:  []quad.Value{vFollows, vStatus},\n\t\t\ttag:     \"pred\",\n\t\t},\n\t\t// Morphism tests\n\t\t{\n\t\t\tmessage: \"simple morphism\",\n\t\t\tpath:    path.StartPath(qs, vCharlie).Follow(grandfollows),\n\t\t\texpect:  []quad.Value{vGreg, vFred, vBob},\n\t\t},\n\t\t{\n\t\t\tmessage: \"reverse morphism\",\n\t\t\tpath:    path.StartPath(qs, vFred).FollowReverse(grandfollows),\n\t\t\texpect:  []quad.Value{vAlice, vCharlie, vDani},\n\t\t},\n\t\t// Context tests\n\t\t{\n\t\t\tmessage: \"query without label limitation\",\n\t\t\tpath:    path.StartPath(qs, vGreg).Out(vStatus),\n\t\t\texpect:  []quad.Value{vSmart, vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \"query with label limitation\",\n\t\t\tpath:    path.StartPath(qs, vGreg).LabelContext(vSmartGraph).Out(vStatus),\n\t\t\texpect:  []quad.Value{vSmart},\n\t\t},\n\t\t{\n\t\t\tmessage: \"reverse context\",\n\t\t\tpath:    path.StartPath(qs, vGreg).Tag(\"base\").LabelContext(vSmartGraph).Out(vStatus).Tag(\"status\").Back(\"base\"),\n\t\t\texpect:  []quad.Value{vGreg},\n\t\t},\n\t\t// Optional tests\n\t\t{\n\t\t\tmessage: \"save limits top level\",\n\t\t\tpath:    path.StartPath(qs, vBob, vCharlie).Out(vFollows).Save(vStatus, \"statustag\"),\n\t\t\texpect:  []quad.Value{vBob, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"optional still returns top level\",\n\t\t\tpath:    path.StartPath(qs, vBob, vCharlie).Out(vFollows).SaveOptional(vStatus, \"statustag\"),\n\t\t\texpect:  []quad.Value{vBob, vFred, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"optional has the appropriate tags\",\n\t\t\tpath:    path.StartPath(qs, vBob, vCharlie).Out(vFollows).SaveOptional(vStatus, \"statustag\"),\n\t\t\ttag:     \"statustag\",\n\t\t\texpect:  []quad.Value{vCool, vCool},\n\t\t},\n\t\t{\n\t\t\tmessage: \"composite paths (clone paths)\",\n\t\t\tpath: func() *path.Path {\n\t\t\t\talicePath := path.StartPath(qs, vAlice)\n\t\t\t\t_ = alicePath.Out(vFollows)\n\n\t\t\t\treturn alicePath\n\t\t\t}(),\n\t\t\texpect: []quad.Value{vAlice},\n\t\t},\n\t\t{\n\t\t\tmessage: \"follow recursive\",\n\t\t\tpath:    path.StartPath(qs, vCharlie).FollowRecursive(vFollows, 0, nil),\n\t\t\texpect:  []quad.Value{vBob, vDani, vFred, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage: \"follow recursive (limit depth)\",\n\t\t\tpath:    path.StartPath(qs, vCharlie).FollowRecursive(vFollows, 1, nil),\n\t\t\texpect:  []quad.Value{vBob, vDani},\n\t\t},\n\t\t{\n\t\t\tmessage: \"find non-existent\",\n\t\t\tpath:    path.StartPath(qs, quad.IRI(\"<not-existing>\")),\n\t\t\texpect:  nil,\n\t\t},\n\t\t{\n\t\t\tmessage: \"use order\",\n\t\t\tpath:    path.StartPath(qs).Order(),\n\t\t\texpect: []quad.Value{\n\t\t\t\tvAlice,\n\t\t\t\tvAre,\n\t\t\t\tvBob,\n\t\t\t\tvCharlie,\n\t\t\t\tvDani,\n\t\t\t\tvEmily,\n\t\t\t\tvFollows,\n\t\t\t\tvFred,\n\t\t\t\tvGreg,\n\t\t\t\tvPredicate,\n\t\t\t\tvSmartGraph,\n\t\t\t\tvStatus,\n\t\t\t\tvCool,\n\t\t\t\tvSmart,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"use order tags\",\n\t\t\tpath:    path.StartPath(qs).Tag(\"target\").Order(),\n\t\t\ttag:     \"target\",\n\t\t\texpect: []quad.Value{\n\t\t\t\tvAlice,\n\t\t\t\tvAre,\n\t\t\t\tvBob,\n\t\t\t\tvCharlie,\n\t\t\t\tvDani,\n\t\t\t\tvEmily,\n\t\t\t\tvFollows,\n\t\t\t\tvFred,\n\t\t\t\tvGreg,\n\t\t\t\tvPredicate,\n\t\t\t\tvSmartGraph,\n\t\t\t\tvStatus,\n\t\t\t\tvCool,\n\t\t\t\tvSmart,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmessage: \"order with a next path\",\n\t\t\tpath:    path.StartPath(qs, vDani, vBob).Save(vFollows, \"target\").Order(),\n\t\t\ttag:     \"target\",\n\t\t\texpect:  []quad.Value{vBob, vFred, vGreg},\n\t\t},\n\t\t{\n\t\t\tmessage:  \"order with a next path\",\n\t\t\tpath:     path.StartPath(qs).Order().Has(vFollows, vBob),\n\t\t\texpect:   []quad.Value{vAlice, vCharlie, vDani},\n\t\t\tunsorted: true,\n\t\t\tskip:     true, // TODO(dennwc): optimize Order in And properly\n\t\t},\n\t\t{\n\t\t\tmessage: \"optional path\",\n\t\t\tpath:    path.StartPath(qs, vBob, vDani, vFred).Optional(path.StartMorphism().Save(vStatus, \"status\")),\n\t\t\ttag:     \"status\",\n\t\t\tempty:   true,\n\t\t\texpect:  []quad.Value{vEmpty, vCool, vCool},\n\t\t},\n\t}\n}\n\nfunc RunTestMorphisms(t *testing.T, fnc testutil.DatabaseFunc) {\n\tfor _, ftest := range []func(*testing.T, testutil.DatabaseFunc){\n\t\ttestFollowRecursive,\n\t\ttestFollowRecursiveHas,\n\t} {\n\t\tftest(t, fnc)\n\t}\n\tqs := makeTestStore(t, fnc)\n\n\tfor _, test := range testSet(qs) {\n\t\tfor _, opt := range []bool{true, false} {\n\t\t\tname := test.message\n\t\t\tif !opt {\n\t\t\t\tname += \" (unoptimized)\"\n\t\t\t}\n\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\tif test.skip {\n\t\t\t\t\tt.SkipNow()\n\t\t\t\t}\n\t\t\t\tvar (\n\t\t\t\t\tgot []quad.Value\n\t\t\t\t\terr error\n\t\t\t\t)\n\t\t\t\tstart := time.Now()\n\t\t\t\tif test.tag == \"\" {\n\t\t\t\t\tgot, err = runTopLevel(qs, test.path, opt)\n\t\t\t\t} else {\n\t\t\t\t\tgot, err = runTag(qs, test.path, test.tag, opt, test.empty)\n\t\t\t\t}\n\t\t\t\tdt := time.Since(start)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !test.unsorted {\n\t\t\t\t\tsort.Sort(quad.ByValueString(got))\n\t\t\t\t}\n\t\t\t\tvar eq bool\n\t\t\t\texp := test.expect\n\t\t\t\tif test.expectAlt != nil {\n\t\t\t\t\tfor _, alt := range test.expectAlt {\n\t\t\t\t\t\texp = alt\n\t\t\t\t\t\tif !test.unsorted {\n\t\t\t\t\t\t\tsort.Sort(quad.ByValueString(exp))\n\t\t\t\t\t\t}\n\t\t\t\t\t\teq = reflect.DeepEqual(got, exp)\n\t\t\t\t\t\tif eq {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif !test.unsorted {\n\t\t\t\t\t\tsort.Sort(quad.ByValueString(test.expect))\n\t\t\t\t\t}\n\t\t\t\t\teq = reflect.DeepEqual(got, test.expect)\n\t\t\t\t}\n\t\t\t\tif !eq {\n\t\t\t\t\tt.Errorf(\"got: %v(%d) expected: %v(%d)\", got, len(got), exp, len(exp))\n\t\t\t\t} else {\n\t\t\t\t\tt.Logf(\"%12v %v\", dt, name)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc testFollowRecursive(t *testing.T, fnc testutil.DatabaseFunc) {\n\tqs := makeTestStore(t, fnc, []quad.Quad{\n\t\tquad.MakeIRI(\"a\", \"parent\", \"b\", \"\"),\n\t\tquad.MakeIRI(\"b\", \"parent\", \"c\", \"\"),\n\t\tquad.MakeIRI(\"c\", \"parent\", \"d\", \"\"),\n\t\tquad.MakeIRI(\"c\", \"labels\", \"tag\", \"\"),\n\t\tquad.MakeIRI(\"d\", \"parent\", \"e\", \"\"),\n\t\tquad.MakeIRI(\"d\", \"labels\", \"tag\", \"\"),\n\t}...)\n\n\tqu := path.StartPath(qs, quad.IRI(\"a\")).FollowRecursive(\n\t\tpath.StartMorphism().Out(quad.IRI(\"parent\")), 0, nil,\n\t).Has(quad.IRI(\"labels\"), quad.IRI(\"tag\"))\n\n\texpect := []quad.Value{quad.IRI(\"c\"), quad.IRI(\"d\")}\n\n\tconst msg = \"follows recursive order\"\n\n\tfor _, opt := range []bool{true, false} {\n\t\tunopt := \"\"\n\t\tif !opt {\n\t\t\tunopt = \" (unoptimized)\"\n\t\t}\n\t\tt.Run(msg+unopt, func(t *testing.T) {\n\t\t\tgot, err := runTopLevel(qs, qu, opt)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Failed to check %s%s: %v\", msg, unopt, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsort.Sort(quad.ByValueString(got))\n\t\t\tsort.Sort(quad.ByValueString(expect))\n\t\t\tif !reflect.DeepEqual(got, expect) {\n\t\t\t\tt.Errorf(\"Failed to %s%s, got: %v(%d) expected: %v(%d)\", msg, unopt, got, len(got), expect, len(expect))\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype byTags struct {\n\ttags []string\n\tarr  []map[string]quad.Value\n}\n\nfunc (b byTags) Len() int {\n\treturn len(b.arr)\n}\n\nfunc (b byTags) Less(i, j int) bool {\n\tm1, m2 := b.arr[i], b.arr[j]\n\tfor _, t := range b.tags {\n\t\tv1, v2 := m1[t], m2[t]\n\t\ts1, s2 := quad.ToString(v1), quad.ToString(v2)\n\t\tif s1 < s2 {\n\t\t\treturn true\n\t\t} else if s1 > s2 {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (b byTags) Swap(i, j int) {\n\tb.arr[i], b.arr[j] = b.arr[j], b.arr[i]\n}\n\nfunc testFollowRecursiveHas(t *testing.T, fnc testutil.DatabaseFunc) {\n\tqs := makeTestStore(t, fnc, []quad.Quad{\n\t\tquad.MakeIRI(\"1\", \"relatesTo\", \"x\", \"\"),\n\t\tquad.MakeIRI(\"2\", \"relatesTo\", \"x\", \"\"),\n\t\tquad.MakeIRI(\"3\", \"relatesTo\", \"y\", \"\"),\n\t\tquad.MakeIRI(\"1\", \"knows\", \"2\", \"\"),\n\t\tquad.MakeIRI(\"2\", \"knows\", \"3\", \"\"),\n\t\tquad.MakeIRI(\"2\", \"knows\", \"1\", \"\"),\n\t}...)\n\n\tqu := path.StartPath(qs, quad.IRI(\"1\")).FollowRecursive(\n\t\tpath.StartMorphism().Tag(\"pid\").Out(quad.IRI(\"knows\")), 2, nil,\n\t).Has(quad.IRI(\"relatesTo\")).Tag(\"id\")\n\n\texpect := []map[string]quad.Value{\n\t\t{\"id\": quad.IRI(\"1\"), \"pid\": quad.IRI(\"2\")},\n\t\t{\"id\": quad.IRI(\"2\"), \"pid\": quad.IRI(\"1\")},\n\t\t{\"id\": quad.IRI(\"3\"), \"pid\": quad.IRI(\"2\")},\n\t}\n\tsortTags := []string{\"id\", \"pid\"}\n\tsort.Sort(byTags{\n\t\ttags: sortTags,\n\t\tarr:  expect,\n\t})\n\n\tconst msg = \"follows recursive loop\"\n\n\tfor _, opt := range []bool{true, false} {\n\t\tunopt := \"\"\n\t\tif !opt {\n\t\t\tunopt = \" (unoptimized)\"\n\t\t}\n\t\tt.Run(msg+unopt, func(t *testing.T) {\n\t\t\tgot, err := runAllTags(qs, qu, opt)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Failed to check %s%s: %v\", msg, unopt, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsort.Sort(byTags{\n\t\t\t\ttags: sortTags,\n\t\t\t\tarr:  got,\n\t\t\t})\n\t\t\trequire.Equal(t, expect, got)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/session.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package query defines the graph session interface general to all query languages.\npackage query\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n)\n\nvar ErrParseMore = errors.New(\"query: more input required\")\n\ntype ErrUnsupportedCollation struct {\n\tCollation Collation\n}\n\nfunc (e *ErrUnsupportedCollation) Error() string {\n\treturn fmt.Sprintf(\"unsupported collation: %v\", e.Collation)\n}\n\n// Iterator for query results.\ntype Iterator interface {\n\t// Next advances the iterator to the next value, which will then be available through\n\t// the Result method. It returns false if no further advancement is possible, or if an\n\t// error was encountered during iteration.  Err should be consulted to distinguish\n\t// between the two cases.\n\tNext(ctx context.Context) bool\n\t// Results returns the current result. The type depends on the collation mode of the query.\n\tResult() interface{}\n\t// Err returns any error that was encountered by the Iterator.\n\tErr() error\n\t// Close the iterator and do internal cleanup.\n\tClose() error\n}\n\n// Collation of results.\ntype Collation int\n\nconst (\n\t// Raw collates results as maps or arrays of graph.Refs or any other query-native or graph-native data type.\n\tRaw = Collation(iota)\n\t// REPL collates results as strings which will be used in CLI.\n\tREPL = Collation(iota)\n\t// JSON collates results as maps, arrays and values, that can be encoded to JSON.\n\tJSON\n\t// JSONLD collates results as maps, arrays and values compatible with JSON-LD spec.\n\tJSONLD\n)\n\n// Options for the query execution.\ntype Options struct {\n\tLimit     int\n\tCollation Collation\n}\n\ntype Session interface {\n\t// Execute runs the query and returns an iterator over the results.\n\t// Type of results depends on Collation. See Options for details.\n\tExecute(ctx context.Context, query string, opt Options) (Iterator, error)\n}\n\ntype REPLSession = Session\n\n// ResponseWriter is a subset of http.ResponseWriter\ntype ResponseWriter interface {\n\tWrite([]byte) (int, error)\n\tWriteHeader(int)\n}\n\n// Language is a description of query language.\ntype Language struct {\n\tName    string\n\tSession func(graph.QuadStore) Session\n\n\t// Custom HTTP handlers\n\n\tHTTPQuery func(ctx context.Context, qs graph.QuadStore, w ResponseWriter, r io.Reader)\n\tHTTPError func(w ResponseWriter, err error)\n}\n\nvar languages = make(map[string]Language)\n\n// RegisterLanguage register a new query language.\nfunc RegisterLanguage(lang Language) {\n\tlanguages[lang.Name] = lang\n}\n\n// NewSession creates a new session for specified query language.\n// It returns nil if language was not registered.\nfunc NewSession(qs graph.QuadStore, lang string) Session {\n\tif l := languages[lang]; l.Session != nil {\n\t\treturn l.Session(qs)\n\t}\n\treturn nil\n}\n\n// GetLanguage returns a query language description.\n// It returns nil if language was not registered.\nfunc GetLanguage(lang string) *Language {\n\tl, ok := languages[lang]\n\tif ok {\n\t\treturn &l\n\t}\n\treturn nil\n}\n\n// Languages returns names of registered query languages.\nfunc Languages() []string {\n\tout := make([]string, 0, len(languages))\n\tfor name := range languages {\n\t\tout = append(out, name)\n\t}\n\treturn out\n}\n\n// Execute runs the query in a given language and returns an iterator over the results.\n// Type of results depends on Collation. See Options for details.\nfunc Execute(ctx context.Context, qs graph.QuadStore, lang, query string, opt Options) (Iterator, error) {\n\tl := GetLanguage(lang)\n\tif l == nil {\n\t\treturn nil, fmt.Errorf(\"unsupported language: %q\", lang)\n\t}\n\tsess := l.Session(qs)\n\treturn sess.Execute(ctx, query, opt)\n}\n"
  },
  {
    "path": "query/sexp/parser.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sexp\n\nimport (\n\t\"context\"\n\n\t\"github.com/badgerodon/peg\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc BuildIteratorTreeForQuery(ctx context.Context, qs graph.QuadStore, query string) iterator.Shape {\n\ts, err := BuildShape(ctx, query)\n\tif err != nil {\n\t\treturn iterator.NewError(err)\n\t}\n\treturn shape.BuildIterator(ctx, qs, s)\n}\n\nfunc BuildShape(ctx context.Context, query string) (shape.Shape, error) {\n\ttree := parseQuery(query)\n\ts, _ := buildShape(tree)\n\ts, _ = shape.Optimize(ctx, s, nil)\n\treturn s, nil\n}\n\nfunc ParseString(input string) string {\n\treturn parseQuery(input).String()\n}\n\nfunc newParser() *peg.Parser {\n\tparser := peg.NewParser()\n\n\tstart := parser.NonTerminal(\"Start\")\n\twhitespace := parser.NonTerminal(\"Whitespace\")\n\tquotedString := parser.NonTerminal(\"QuotedString\")\n\trootConstraint := parser.NonTerminal(\"RootConstraint\")\n\n\tconstraint := parser.NonTerminal(\"Constraint\")\n\tcolonIdentifier := parser.NonTerminal(\"ColonIdentifier\")\n\tvariable := parser.NonTerminal(\"Variable\")\n\tidentifier := parser.NonTerminal(\"Identifier\")\n\tfixedNode := parser.NonTerminal(\"FixedNode\")\n\tnodeIdent := parser.NonTerminal(\"NodeIdentifier\")\n\tpredIdent := parser.NonTerminal(\"PredIdentifier\")\n\treverse := parser.NonTerminal(\"Reverse\")\n\tpredKeyword := parser.NonTerminal(\"PredicateKeyword\")\n\toptional := parser.NonTerminal(\"OptionalKeyword\")\n\n\tstart.Expression = rootConstraint\n\n\twhitespace.Expression = parser.OneOrMore(\n\t\tparser.OrderedChoice(\n\t\t\tparser.Terminal(' '),\n\t\t\tparser.Terminal('\\t'),\n\t\t\tparser.Terminal('\\n'),\n\t\t\tparser.Terminal('\\r'),\n\t\t),\n\t)\n\n\tquotedString.Expression = parser.Sequence(\n\t\tparser.Terminal('\"'),\n\t\tparser.OneOrMore(\n\t\t\tparser.OrderedChoice(\n\t\t\t\tparser.Range('0', '9'),\n\t\t\t\tparser.Range('a', 'z'),\n\t\t\t\tparser.Range('A', 'Z'),\n\t\t\t\tparser.Terminal('_'),\n\t\t\t\tparser.Terminal('/'),\n\t\t\t\tparser.Terminal(':'),\n\t\t\t\tparser.Terminal(' '),\n\t\t\t\tparser.Terminal('\\''),\n\t\t\t),\n\t\t),\n\t\tparser.Terminal('\"'),\n\t)\n\n\tpredKeyword.Expression = parser.OrderedChoice(\n\t\toptional,\n\t)\n\n\toptional.Expression = parser.Sequence(\n\t\tparser.Terminal('o'),\n\t\tparser.Terminal('p'),\n\t\tparser.Terminal('t'),\n\t\tparser.Terminal('i'),\n\t\tparser.Terminal('o'),\n\t\tparser.Terminal('n'),\n\t\tparser.Terminal('a'),\n\t\tparser.Terminal('l'),\n\t)\n\n\tidentifier.Expression = parser.OneOrMore(\n\t\tparser.OrderedChoice(\n\t\t\tparser.Range('0', '9'),\n\t\t\tparser.Range('a', 'z'),\n\t\t\tparser.Range('A', 'Z'),\n\t\t\tparser.Terminal('_'),\n\t\t\tparser.Terminal('.'),\n\t\t\tparser.Terminal('/'),\n\t\t\tparser.Terminal(':'),\n\t\t\tparser.Terminal('#'),\n\t\t),\n\t)\n\n\treverse.Expression = parser.Terminal('!')\n\n\tvariable.Expression = parser.Sequence(\n\t\tparser.Terminal('$'),\n\t\tidentifier,\n\t)\n\n\tcolonIdentifier.Expression = parser.Sequence(\n\t\tparser.Terminal(':'),\n\t\tidentifier,\n\t)\n\n\tfixedNode.Expression = parser.OrderedChoice(\n\t\tcolonIdentifier,\n\t\tquotedString,\n\t)\n\n\tnodeIdent.Expression = parser.OrderedChoice(\n\t\tvariable,\n\t\tfixedNode,\n\t)\n\n\tpredIdent.Expression = parser.Sequence(\n\t\tparser.Optional(reverse),\n\t\tparser.OrderedChoice(\n\t\t\tnodeIdent,\n\t\t\tconstraint,\n\t\t),\n\t)\n\n\tconstraint.Expression = parser.Sequence(\n\t\tparser.Terminal('('),\n\t\tparser.Optional(whitespace),\n\t\tpredIdent,\n\t\tparser.Optional(whitespace),\n\t\tparser.Optional(predKeyword),\n\t\tparser.Optional(whitespace),\n\t\tparser.OrderedChoice(\n\t\t\tnodeIdent,\n\t\t\trootConstraint,\n\t\t),\n\t\tparser.Optional(whitespace),\n\t\tparser.Terminal(')'),\n\t)\n\n\trootConstraint.Expression = parser.Sequence(\n\t\tparser.Terminal('('),\n\t\tparser.Optional(whitespace),\n\t\tnodeIdent,\n\t\tparser.Optional(whitespace),\n\t\tparser.ZeroOrMore(parser.Sequence(\n\t\t\tconstraint,\n\t\t\tparser.Optional(whitespace),\n\t\t)),\n\t\tparser.Terminal(')'),\n\t)\n\treturn parser\n}\n\nfunc parseQuery(input string) *peg.ExpressionTree {\n\treturn newParser().Parse(input)\n}\n\nfunc getIdentString(tree *peg.ExpressionTree) string {\n\tout := \"\"\n\tif len(tree.Children) > 0 {\n\t\tfor _, child := range tree.Children {\n\t\t\tout += getIdentString(child)\n\t\t}\n\t} else {\n\t\tif tree.Value != '\"' {\n\t\t\tout += string([]rune{rune(tree.Value)})\n\t\t}\n\t}\n\treturn out\n}\n\nfunc lookup(s string) shape.Shape {\n\treturn shape.Lookup{quad.StringToValue(s)}\n}\n\nfunc buildShape(tree *peg.ExpressionTree) (_ shape.Shape, opt bool) {\n\tswitch tree.Name {\n\tcase \"Start\":\n\t\treturn buildShape(tree.Children[0])\n\tcase \"NodeIdentifier\":\n\t\tvar out shape.Shape\n\t\tnodeID := getIdentString(tree)\n\t\tif tree.Children[0].Name == \"Variable\" {\n\t\t\tout = shape.Save{\n\t\t\t\tFrom: shape.AllNodes{},\n\t\t\t\tTags: []string{nodeID},\n\t\t\t}\n\t\t} else {\n\t\t\tn := nodeID\n\t\t\tif tree.Children[0].Children[0].Name == \"ColonIdentifier\" {\n\t\t\t\tn = nodeID[1:]\n\t\t\t}\n\t\t\tout = lookup(n)\n\t\t}\n\t\treturn out, false\n\tcase \"PredIdentifier\":\n\t\ti := 0\n\t\tif tree.Children[0].Name == \"Reverse\" {\n\t\t\t//Taken care of below\n\t\t\ti++\n\t\t}\n\t\tit, _ := buildShape(tree.Children[i])\n\t\treturn shape.Quads{\n\t\t\t{Dir: quad.Predicate, Values: it},\n\t\t}, false\n\tcase \"RootConstraint\":\n\t\tvar and shape.IntersectOpt\n\t\tfor _, c := range tree.Children {\n\t\t\tswitch c.Name {\n\t\t\tcase \"NodeIdentifier\":\n\t\t\t\tfallthrough\n\t\t\tcase \"Constraint\":\n\t\t\t\tit, opt := buildShape(c)\n\t\t\t\tif opt {\n\t\t\t\t\tand.AddOptional(it)\n\t\t\t\t} else {\n\t\t\t\t\tand.Add(it)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn and, false\n\tcase \"Constraint\":\n\t\ttopLevelDir := quad.Subject\n\t\tsubItDir := quad.Object\n\t\tvar subAnd shape.IntersectOpt\n\t\tisOptional := false\n\t\tfor _, c := range tree.Children {\n\t\t\tswitch c.Name {\n\t\t\tcase \"PredIdentifier\":\n\t\t\t\tif c.Children[0].Name == \"Reverse\" {\n\t\t\t\t\ttopLevelDir = quad.Object\n\t\t\t\t\tsubItDir = quad.Subject\n\t\t\t\t}\n\t\t\t\tit, opt := buildShape(c)\n\t\t\t\tif opt {\n\t\t\t\t\tsubAnd.AddOptional(it)\n\t\t\t\t} else {\n\t\t\t\t\tsubAnd.Add(it)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\tcase \"PredicateKeyword\":\n\t\t\t\tswitch c.Children[0].Name {\n\t\t\t\tcase \"OptionalKeyword\":\n\t\t\t\t\tisOptional = true\n\t\t\t\t}\n\t\t\tcase \"NodeIdentifier\":\n\t\t\t\tfallthrough\n\t\t\tcase \"RootConstraint\":\n\t\t\t\tit, opt := buildShape(c)\n\t\t\t\tl := shape.Quads{\n\t\t\t\t\t{Dir: subItDir, Values: it},\n\t\t\t\t}\n\t\t\t\tif opt {\n\t\t\t\t\tsubAnd.AddOptional(l)\n\t\t\t\t} else {\n\t\t\t\t\tsubAnd.Add(l)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn shape.NodesFrom{\n\t\t\tDir:   topLevelDir,\n\t\t\tQuads: subAnd,\n\t\t}, isOptional\n\tdefault:\n\t\treturn shape.Null{}, false\n\t}\n}\n"
  },
  {
    "path": "query/sexp/parser_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sexp\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph/graphtest/testutil\"\n\t_ \"github.com/cayleygraph/cayley/graph/memstore\"\n\tsh \"github.com/cayleygraph/cayley/query/shape\"\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBadParse(t *testing.T) {\n\tstr := ParseString(\"()\")\n\tif str != \"\" {\n\t\tt.Errorf(\"Unexpected parse result, got:%q\", str)\n\t}\n}\n\nvar (\n\tquads1 = []quad.Quad{quad.Make(\"i\", \"can\", \"win\", nil)}\n)\n\nvar testQueries = []struct {\n\tmessage string\n\tadd     []quad.Quad\n\tquery   string\n\tshape   sh.Shape\n\texpect  string\n\ttags    map[string]string\n}{\n\t{\n\t\tmessage: \"empty\",\n\t\tquery:   \"()\",\n\t\tshape:   sh.Null{},\n\t},\n\t{\n\t\tmessage: \"get a single quad linkage\",\n\t\tadd:     quads1,\n\t\tquery:   \"($a (:can \\\"win\\\"))\",\n\t\tshape: sh.Save{\n\t\t\tTags: []string{\"$a\"},\n\t\t\tFrom: sh.NodesFrom{\n\t\t\t\tDir: quad.Subject,\n\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"can\")},\n\t\t\t\t\t{Dir: quad.Object, Values: lookup(\"win\")},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\texpect: \"i\",\n\t},\n\t{\n\t\tmessage: \"get a single quad linkage (internal)\",\n\t\tadd:     quads1,\n\t\tquery:   \"(\\\"i\\\" (:can $a))\",\n\t\tshape: sh.Intersect{\n\t\t\tlookup(\"i\"),\n\t\t\tsh.NodesFrom{\n\t\t\t\tDir: quad.Subject,\n\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"can\")},\n\t\t\t\t\t{\n\t\t\t\t\t\tDir: quad.Object, Values: sh.Save{\n\t\t\t\t\t\t\tTags: []string{\"$a\"},\n\t\t\t\t\t\t\tFrom: sh.AllNodes{},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\texpect: \"i\",\n\t},\n\t{\n\t\tmessage: \"tree constraint\",\n\t\tadd: []quad.Quad{\n\t\t\tquad.Make(\"i\", \"like\", \"food\", nil),\n\t\t\tquad.Make(\"food\", \"is\", \"good\", nil),\n\t\t},\n\t\tquery: \"(\\\"i\\\"\\n\" +\n\t\t\t\"(:like\\n\" +\n\t\t\t\"($a (:is :good))))\",\n\t\tshape: sh.Intersect{\n\t\t\tlookup(\"i\"),\n\t\t\tsh.NodesFrom{\n\t\t\t\tDir: quad.Subject,\n\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"like\")},\n\t\t\t\t\t{\n\t\t\t\t\t\tDir: quad.Object, Values: sh.Save{\n\t\t\t\t\t\t\tTags: []string{\"$a\"},\n\t\t\t\t\t\t\tFrom: sh.NodesFrom{\n\t\t\t\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"is\")},\n\t\t\t\t\t\t\t\t\t{Dir: quad.Object, Values: lookup(\"good\")},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\texpect: \"i\",\n\t\ttags: map[string]string{\n\t\t\t\"$a\": \"food\",\n\t\t},\n\t},\n\t{\n\t\tmessage: \"multiple constraint\",\n\t\tadd: []quad.Quad{\n\t\t\tquad.Make(\"i\", \"like\", \"food\", nil),\n\t\t\tquad.Make(\"i\", \"like\", \"beer\", nil),\n\t\t\tquad.Make(\"you\", \"like\", \"beer\", nil),\n\t\t},\n\t\tquery: `(\n\t\t\t$a\n\t\t\t(:like :beer)\n\t\t\t(:like \"food\")\n\t\t)`,\n\t\tshape: sh.Save{\n\t\t\tTags: []string{\"$a\"},\n\t\t\tFrom: sh.Intersect{\n\t\t\t\tsh.NodesFrom{\n\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"like\")},\n\t\t\t\t\t\t{Dir: quad.Object, Values: lookup(\"beer\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tsh.NodesFrom{\n\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\tQuads: sh.Quads{\n\t\t\t\t\t\t{Dir: quad.Predicate, Values: lookup(\"like\")},\n\t\t\t\t\t\t{Dir: quad.Object, Values: lookup(\"food\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\texpect: \"i\",\n\t},\n}\n\nfunc TestSexp(t *testing.T) {\n\tctx := context.TODO()\n\tfor _, test := range testQueries {\n\t\tt.Run(test.message, func(t *testing.T) {\n\t\t\tqs, _ := graph.NewQuadStore(\"memstore\", \"\", nil)\n\t\t\t_ = testutil.MakeWriter(t, qs, nil, test.add...)\n\n\t\t\ts, _ := BuildShape(ctx, test.query)\n\t\t\trequire.Equal(t, test.shape, s, \"%s\\n%#v\\nvs\\n%#v\", test.message, test.shape, s)\n\n\t\t\tit := BuildIteratorTreeForQuery(ctx, qs, test.query).Iterate()\n\t\t\tif it.Next(ctx) != (test.expect != \"\") {\n\t\t\t\tt.Errorf(\"Failed to %s\", test.message)\n\t\t\t}\n\t\t\tif test.expect != \"\" {\n\t\t\t\tqv, err := qs.ValueOf(quad.StringToValue(test.expect))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, qv, it.Result())\n\n\t\t\t\ttags := make(map[string]graph.Ref)\n\t\t\t\tit.TagResults(tags)\n\t\t\t\tfor k, v := range test.tags {\n\t\t\t\t\tname, err := qs.NameOf(tags[k])\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Equal(t, v, quad.ToString(name))\n\t\t\t\t}\n\t\t\t\tif it.Next(ctx) {\n\t\t\t\t\tt.Error(\"too many results\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "query/sexp/session.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sexp\n\n// Defines a running session of the sexp query language.\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/query\"\n)\n\nconst Name = \"sexp\"\n\nfunc init() {\n\tquery.RegisterLanguage(query.Language{\n\t\tName: Name,\n\t\tSession: func(qs graph.QuadStore) query.Session {\n\t\t\treturn NewSession(qs)\n\t\t},\n\t})\n}\n\ntype Session struct {\n\tqs graph.QuadStore\n}\n\nfunc NewSession(qs graph.QuadStore) *Session {\n\treturn &Session{qs: qs}\n}\n\nfunc (s *Session) Parse(input string) error {\n\tvar parenDepth int\n\tfor i, x := range input {\n\t\tif x == '(' {\n\t\t\tparenDepth++\n\t\t}\n\t\tif x == ')' {\n\t\t\tparenDepth--\n\t\t\tif parenDepth < 0 {\n\t\t\t\tmin := 0\n\t\t\t\tif (i - 10) > min {\n\t\t\t\t\tmin = i - 10\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"too many close parentheses at char %d: %s\", i, input[min:i])\n\t\t\t}\n\t\t}\n\t}\n\tif parenDepth > 0 {\n\t\treturn query.ErrParseMore\n\t}\n\tif len(ParseString(input)) > 0 {\n\t\treturn nil\n\t}\n\treturn errors.New(\"invalid syntax\")\n}\n\nfunc (s *Session) Execute(ctx context.Context, input string, opt query.Options) (query.Iterator, error) {\n\tswitch opt.Collation {\n\tcase query.Raw, query.REPL:\n\tdefault:\n\t\treturn nil, &query.ErrUnsupportedCollation{Collation: opt.Collation}\n\t}\n\tit := BuildIteratorTreeForQuery(ctx, s.qs, input).Iterate()\n\tif err := it.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\tif opt.Limit > 0 {\n\t\tit = iterator.NewLimitNext(it, int64(opt.Limit))\n\t}\n\treturn &results{\n\t\ts:   s,\n\t\tcol: opt.Collation,\n\t\tit:  it,\n\t}, nil\n}\n\ntype results struct {\n\ts        *Session\n\tcol      query.Collation\n\tit       iterator.Scanner\n\tnextPath bool\n\terr      error\n}\n\nfunc (it *results) Next(ctx context.Context) bool {\n\tif it.nextPath && it.it.NextPath(ctx) {\n\t\treturn true\n\t}\n\tit.nextPath = false\n\tif it.it.Next(ctx) {\n\t\tit.nextPath = true\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (it *results) Result() interface{} {\n\tm := make(map[string]graph.Ref)\n\tit.it.TagResults(m)\n\tif it.col == query.Raw {\n\t\treturn m\n\t}\n\tout := \"****\\n\"\n\ttagKeys := make([]string, len(m))\n\ti := 0\n\tfor k := range m {\n\t\ttagKeys[i] = k\n\t\ti++\n\t}\n\tsort.Strings(tagKeys)\n\tfor _, k := range tagKeys {\n\t\tif k == \"$_\" {\n\t\t\tcontinue\n\t\t}\n\t\tknv, err := it.s.qs.NameOf(m[k])\n\t\tif err != nil {\n\t\t\tit.err = err\n\t\t\treturn nil\n\t\t}\n\t\tout += fmt.Sprintf(\"%s : %s\\n\", k, knv)\n\t}\n\treturn out\n}\n\nfunc (it *results) Err() error {\n\treturn it.it.Err()\n}\n\nfunc (it *results) Close() error {\n\treturn it.it.Close()\n}\n"
  },
  {
    "path": "query/shape/path.go",
    "content": "package shape\n\nimport (\n\t\"context\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc IntersectShapes(s1, s2 Shape) Shape {\n\tswitch s1 := s1.(type) {\n\tcase AllNodes:\n\t\treturn s2\n\tcase Intersect:\n\t\tif s2, ok := s2.(Intersect); ok {\n\t\t\treturn append(s1, s2...)\n\t\t}\n\t\treturn append(s1, s2)\n\t}\n\treturn Intersect{s1, s2}\n}\n\nfunc IntersectOptional(s, opt Shape) Shape {\n\tvar optional []Shape\n\tswitch opt := opt.(type) {\n\tcase Intersect:\n\t\toptional = []Shape(opt)\n\tcase IntersectOpt:\n\t\toptional = make([]Shape, 0, len(opt.Sub)+len(opt.Opt))\n\t\toptional = append(optional, opt.Sub...)\n\t\toptional = append(optional, opt.Opt...)\n\tdefault:\n\t\toptional = []Shape{opt}\n\t}\n\tif len(optional) == 0 {\n\t\treturn s\n\t}\n\tswitch s := s.(type) {\n\tcase Intersect:\n\t\treturn IntersectOpt{Sub: s, Opt: optional}\n\tcase IntersectOpt:\n\t\ts.Opt = append(s.Opt, optional...)\n\t\treturn s\n\t}\n\treturn IntersectOpt{Sub: Intersect{s}, Opt: optional}\n}\n\nfunc UnionShapes(s1, s2 Shape) Union {\n\tif s1, ok := s1.(Union); ok {\n\t\tif s2, ok := s2.(Union); ok {\n\t\t\treturn append(s1, s2...)\n\t\t}\n\t\treturn append(s1, s2)\n\t}\n\treturn Union{s1, s2}\n}\n\nfunc buildOut(from, via, labels Shape, tags []string, in bool) Shape {\n\tstart, goal := quad.Subject, quad.Object\n\tif in {\n\t\tstart, goal = goal, start\n\t}\n\tif len(tags) != 0 {\n\t\tvia = Save{From: via, Tags: tags}\n\t}\n\n\tquads := make(Quads, 0, 3)\n\tif _, ok := from.(AllNodes); !ok {\n\t\tquads = append(quads, QuadFilter{\n\t\t\tDir: start, Values: from,\n\t\t})\n\t}\n\tif _, ok := via.(AllNodes); !ok {\n\t\tquads = append(quads, QuadFilter{\n\t\t\tDir: quad.Predicate, Values: via,\n\t\t})\n\t}\n\tif labels != nil {\n\t\tif _, ok := labels.(AllNodes); !ok {\n\t\t\tquads = append(quads, QuadFilter{\n\t\t\t\tDir: quad.Label, Values: labels,\n\t\t\t})\n\t\t}\n\t}\n\treturn NodesFrom{Quads: quads, Dir: goal}\n}\n\nfunc Out(from, via, labels Shape, tags ...string) Shape {\n\treturn buildOut(from, via, labels, tags, false)\n}\n\nfunc In(from, via, labels Shape, tags ...string) Shape {\n\treturn buildOut(from, via, labels, tags, true)\n}\n\n// InWithTags, OutWithTags, Both, BothWithTags\n\nfunc Predicates(from Shape, in bool) Shape {\n\tdir := quad.Subject\n\tif in {\n\t\tdir = quad.Object\n\t}\n\treturn Unique{NodesFrom{\n\t\tQuads: Quads{\n\t\t\t{Dir: dir, Values: from},\n\t\t},\n\t\tDir: quad.Predicate,\n\t}}\n}\n\nfunc SavePredicates(from Shape, in bool, tag string) Shape {\n\tpreds := Save{\n\t\tFrom: AllNodes{},\n\t\tTags: []string{tag},\n\t}\n\tstart := quad.Subject\n\tif in {\n\t\tstart = quad.Object\n\t}\n\n\tvar save Shape = NodesFrom{\n\t\tQuads: Quads{\n\t\t\t{Dir: quad.Predicate, Values: preds},\n\t\t},\n\t\tDir: start,\n\t}\n\treturn IntersectShapes(from, save)\n}\n\nfunc Labels(from Shape) Shape {\n\treturn Unique{NodesFrom{\n\t\tQuads: Union{\n\t\t\tQuads{\n\t\t\t\t{Dir: quad.Subject, Values: from},\n\t\t\t},\n\t\t\tQuads{\n\t\t\t\t{Dir: quad.Object, Values: from},\n\t\t\t},\n\t\t},\n\t\tDir: quad.Label,\n\t}}\n}\n\nfunc SaveVia(from, via Shape, tag string, rev, opt bool) Shape {\n\treturn SaveViaLabels(from, via, AllNodes{}, tag, rev, opt)\n}\n\nfunc SaveViaLabels(from, via, labels Shape, tag string, rev, opt bool) Shape {\n\tnodes := Save{\n\t\tFrom: AllNodes{},\n\t\tTags: []string{tag},\n\t}\n\tstart, goal := quad.Subject, quad.Object\n\tif rev {\n\t\tstart, goal = goal, start\n\t}\n\n\tquads := Quads{\n\t\t{Dir: goal, Values: nodes},\n\t\t{Dir: quad.Predicate, Values: via},\n\t}\n\tif labels != nil {\n\t\tif _, ok := labels.(AllNodes); !ok {\n\t\t\tquads = append(quads, QuadFilter{\n\t\t\t\tDir: quad.Label, Values: labels,\n\t\t\t})\n\t\t}\n\t}\n\n\tvar save Shape = NodesFrom{\n\t\tQuads: quads,\n\t\tDir:   start,\n\t}\n\tif opt {\n\t\treturn IntersectOptional(from, save)\n\t}\n\treturn IntersectShapes(from, save)\n}\n\nfunc Has(from, via, nodes Shape, rev bool) Shape {\n\treturn HasLabels(from, via, AllNodes{}, nodes, rev)\n}\n\nfunc HasLabels(from, via, nodes, labels Shape, rev bool) Shape {\n\tstart, goal := quad.Subject, quad.Object\n\tif rev {\n\t\tstart, goal = goal, start\n\t}\n\n\tquads := make(Quads, 0, 3)\n\tif _, ok := nodes.(AllNodes); !ok {\n\t\tquads = append(quads, QuadFilter{\n\t\t\tDir: goal, Values: nodes,\n\t\t})\n\t}\n\tif _, ok := via.(AllNodes); !ok {\n\t\tquads = append(quads, QuadFilter{\n\t\t\tDir: quad.Predicate, Values: via,\n\t\t})\n\t}\n\tif labels != nil {\n\t\tif _, ok := labels.(AllNodes); !ok {\n\t\t\tquads = append(quads, QuadFilter{\n\t\t\t\tDir: quad.Label, Values: labels,\n\t\t\t})\n\t\t}\n\t}\n\tif len(quads) == 0 {\n\t\tpanic(\"empty has\")\n\t}\n\treturn IntersectShapes(from, NodesFrom{\n\t\tQuads: quads, Dir: start,\n\t})\n}\n\nfunc AddFilters(nodes Shape, filters ...ValueFilter) Shape {\n\tif len(filters) == 0 {\n\t\treturn nodes\n\t}\n\tif s, ok := nodes.(Filter); ok {\n\t\tarr := make([]ValueFilter, 0, len(s.Filters)+len(filters))\n\t\tarr = append(arr, s.Filters...)\n\t\tarr = append(arr, filters...)\n\t\treturn Filter{From: s.From, Filters: arr}\n\t}\n\tif nodes == nil {\n\t\tnodes = AllNodes{}\n\t}\n\treturn Filter{\n\t\tFrom:    nodes,\n\t\tFilters: filters,\n\t}\n}\n\nfunc Compare(nodes Shape, op iterator.Operator, v quad.Value) Shape {\n\treturn AddFilters(nodes, Comparison{Op: op, Val: v})\n}\n\nfunc Iterate(ctx context.Context, qs graph.QuadStore, s Shape) *iterator.Chain {\n\tit := BuildIterator(ctx, qs, s)\n\treturn iterator.Iterate(ctx, it).On(qs)\n}\n"
  },
  {
    "path": "query/shape/shape.go",
    "content": "package shape\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n)\n\nvar (\n\tdebugShapes    = os.Getenv(\"CAYLEY_DEBUG_SHAPES\") == \"true\"\n\tdebugOptimizer = os.Getenv(\"CAYLEY_DEBUG_OPTIMIZER\") == \"true\"\n)\n\n// Shape represent a query tree shape.\ntype Shape interface {\n\t// BuildIterator constructs an iterator tree from a given shapes and binds it to QuadStore.\n\tBuildIterator(qs graph.QuadStore) iterator.Shape\n\t// Optimize runs an optimization pass over a query shape.\n\t//\n\t// It returns a bool that indicates if shape was replaced and should always return a copy of shape in this case.\n\t// In case no optimizations were made, it returns the same unmodified shape.\n\t//\n\t// If Optimizer is specified, it will be used instead of default optimizations.\n\tOptimize(ctx context.Context, r Optimizer) (Shape, bool)\n}\n\ntype Optimizer interface {\n\tOptimizeShape(ctx context.Context, s Shape) (Shape, bool)\n}\n\n// Composite shape can be simplified to a tree of more basic shapes.\ntype Composite interface {\n\tSimplify() Shape\n}\n\n// WalkFunc is used to visit all shapes in the tree.\n// If false is returned, branch will not be traversed further.\ntype WalkFunc func(Shape) bool\n\ntype resolveValues struct {\n\tqs graph.QuadStore\n}\n\nfunc (r resolveValues) OptimizeShape(ctx context.Context, s Shape) (Shape, bool) {\n\tif l, ok := s.(Lookup); ok {\n\t\tlv, err := l.resolve(r.qs)\n\t\tif err == nil {\n\t\t\treturn lv, true\n\t\t}\n\t}\n\treturn s, false\n}\n\n// Optimize applies generic optimizations for the tree.\n// If quad store is specified it will also resolve Lookups and apply any specific optimizations.\n// Should not be used with Simplify - it will fold query to a compact form again.\nfunc Optimize(ctx context.Context, s Shape, qs graph.QuadStore) (Shape, bool) {\n\tif s == nil {\n\t\treturn nil, false\n\t}\n\tqs = graph.Unwrap(qs)\n\tvar opt bool\n\tif qs != nil {\n\t\t// resolve all lookups earlier\n\t\ts, opt = s.Optimize(ctx, resolveValues{qs: qs})\n\t}\n\tif s == nil {\n\t\treturn Null{}, true\n\t}\n\t// generic optimizations\n\tvar opt1 bool\n\ts, opt1 = s.Optimize(ctx, nil)\n\tif s == nil {\n\t\treturn Null{}, true\n\t}\n\topt = opt || opt1\n\t// apply quadstore-specific optimizations\n\tif so, ok := qs.(Optimizer); ok && s != nil {\n\t\tvar opt2 bool\n\t\ts, opt2 = s.Optimize(ctx, so)\n\t\topt = opt || opt2\n\t}\n\tif s == nil {\n\t\treturn Null{}, true\n\t}\n\treturn s, opt\n}\n\nvar rtShape = reflect.TypeOf((*Shape)(nil)).Elem()\n\n// Walk calls provided function for each shape in the tree.\nfunc Walk(s Shape, fnc WalkFunc) {\n\tif s == nil {\n\t\treturn\n\t}\n\tif !fnc(s) {\n\t\treturn\n\t}\n\twalkReflect(reflect.ValueOf(s), fnc)\n}\n\nfunc walkReflect(rv reflect.Value, fnc WalkFunc) {\n\trt := rv.Type()\n\tswitch rv.Kind() {\n\tcase reflect.Slice:\n\t\tif rt.Elem().ConvertibleTo(rtShape) {\n\t\t\t// all element are shapes - call function on each of them\n\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\tWalk(rv.Index(i).Interface().(Shape), fnc)\n\t\t\t}\n\t\t} else {\n\t\t\t// elements are not shapes, but might contain them\n\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\twalkReflect(rv.Index(i), fnc)\n\t\t\t}\n\t\t}\n\tcase reflect.Map:\n\t\tkeys := rv.MapKeys()\n\t\tif rt.Elem().ConvertibleTo(rtShape) {\n\t\t\t// all element are shapes - call function on each of them\n\t\t\tfor _, k := range keys {\n\t\t\t\tWalk(rv.MapIndex(k).Interface().(Shape), fnc)\n\t\t\t}\n\t\t} else {\n\t\t\t// elements are not shapes, but might contain them\n\t\t\tfor _, k := range keys {\n\t\t\t\twalkReflect(rv.MapIndex(k), fnc)\n\t\t\t}\n\t\t}\n\tcase reflect.Struct:\n\t\t// visit all fields\n\t\tfor i := 0; i < rt.NumField(); i++ {\n\t\t\tf := rt.Field(i)\n\t\t\t// if field is of shape type - call function on it\n\t\t\t// we skip anonymous fields because they were already visited as part of the parent\n\t\t\tif !f.Anonymous && f.Type.ConvertibleTo(rtShape) {\n\t\t\t\tWalk(rv.Field(i).Interface().(Shape), fnc)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// it might be a struct/map/slice field, so we need to go deeper\n\t\t\twalkReflect(rv.Field(i), fnc)\n\t\t}\n\t}\n}\n\n// InternalQuad is an internal representation of quad index in QuadStore.\ntype InternalQuad struct {\n\tSubject   refs.Ref\n\tPredicate refs.Ref\n\tObject    refs.Ref\n\tLabel     refs.Ref\n}\n\n// Get returns a specified direction of the quad.\nfunc (q InternalQuad) Get(d quad.Direction) refs.Ref {\n\tswitch d {\n\tcase quad.Subject:\n\t\treturn q.Subject\n\tcase quad.Predicate:\n\t\treturn q.Predicate\n\tcase quad.Object:\n\t\treturn q.Object\n\tcase quad.Label:\n\t\treturn q.Label\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// Set assigns a specified direction of the quad to a given value.\nfunc (q *InternalQuad) Set(d quad.Direction, v refs.Ref) {\n\tswitch d {\n\tcase quad.Subject:\n\t\tq.Subject = v\n\tcase quad.Predicate:\n\t\tq.Predicate = v\n\tcase quad.Object:\n\t\tq.Object = v\n\tcase quad.Label:\n\t\tq.Label = v\n\tdefault:\n\t\tpanic(d)\n\t}\n}\n\n// QuadIndexer is an optional interface for quad stores that keep an index of quad directions.\n//\n// It is used to optimize shapes based on stats from these indexes.\ntype QuadIndexer interface {\n\t// SizeOfIndex returns a size of a quad index with given constraints.\n\tSizeOfIndex(c map[quad.Direction]refs.Ref) (int64, bool)\n\t// LookupQuadIndex finds a quad that matches a given constraint.\n\t// It returns false if quad was not found, or there are multiple quads matching constraint.\n\tLookupQuadIndex(c map[quad.Direction]refs.Ref) (InternalQuad, bool)\n}\n\n// IsNull safely checks if shape represents an empty set. It accounts for both Null and nil.\nfunc IsNull(s Shape) bool {\n\t_, ok := s.(Null)\n\treturn s == nil || ok\n}\n\n// BuildIterator optimizes the shape and builds a corresponding iterator tree.\nfunc BuildIterator(ctx context.Context, qs graph.QuadStore, s Shape) iterator.Shape {\n\tqs = graph.Unwrap(qs)\n\tif s != nil {\n\t\tif debugShapes || clog.V(2) {\n\t\t\tclog.Infof(\"shape: %#v\", s)\n\t\t}\n\t\ts, _ = Optimize(ctx, s, qs)\n\t\tif debugOptimizer || clog.V(2) {\n\t\t\tclog.Infof(\"optimized: %#v\", s)\n\t\t}\n\t}\n\tif IsNull(s) {\n\t\treturn iterator.NewNull()\n\t}\n\treturn s.BuildIterator(qs)\n}\n\n// Null represent an empty set. Mostly used as a safe alias for nil shape.\ntype Null struct{}\n\nfunc (Null) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\treturn iterator.NewNull()\n}\nfunc (s Null) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif r != nil {\n\t\treturn r.OptimizeShape(ctx, s)\n\t}\n\treturn nil, true\n}\n\n// AllNodes represents all nodes in QuadStore.\ntype AllNodes struct{}\n\nfunc (s AllNodes) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\treturn qs.NodesAllIterator()\n}\nfunc (s AllNodes) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif r != nil {\n\t\treturn r.OptimizeShape(ctx, s)\n\t}\n\treturn s, false\n}\n\n// Except excludes a set on nodes from a source. If source is nil, AllNodes is assumed.\ntype Except struct {\n\tExclude Shape // nodes to exclude\n\tFrom    Shape // a set of all nodes to exclude from; nil means AllNodes\n}\n\nfunc (s Except) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tvar all iterator.Shape\n\tif s.From != nil {\n\t\tall = s.From.BuildIterator(qs)\n\t} else {\n\t\tall = qs.NodesAllIterator()\n\t}\n\tif IsNull(s.Exclude) {\n\t\treturn all\n\t}\n\treturn iterator.NewNot(s.Exclude.BuildIterator(qs), all)\n}\nfunc (s Except) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tvar opt bool\n\ts.Exclude, opt = s.Exclude.Optimize(ctx, r)\n\tif s.From != nil {\n\t\tvar opta bool\n\t\ts.From, opta = s.From.Optimize(ctx, r)\n\t\topt = opt || opta\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\tif IsNull(s.Exclude) {\n\t\treturn AllNodes{}, true\n\t} else if _, ok := s.Exclude.(AllNodes); ok {\n\t\treturn nil, true\n\t}\n\treturn s, opt\n}\n\n// ValueFilter is an interface for iterator wrappers that can filter node values.\ntype ValueFilter interface {\n\tBuildIterator(qs graph.QuadStore, it iterator.Shape) iterator.Shape\n}\n\n// Filter filters all values from the source using a list of operations.\ntype Filter struct {\n\tFrom    Shape         // source that will be filtered\n\tFilters []ValueFilter // filters to apply\n}\n\nfunc (s Filter) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.From) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.From.BuildIterator(qs)\n\tfor _, f := range s.Filters {\n\t\tit = f.BuildIterator(qs, it)\n\t}\n\treturn it\n}\nfunc (s Filter) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.From, opt = s.From.Optimize(ctx, r)\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t} else if len(s.Filters) == 0 {\n\t\treturn s.From, true\n\t}\n\treturn s, opt\n}\n\nvar _ ValueFilter = Comparison{}\n\n// Comparison is a value filter that evaluates binary operation in reference to a fixed value.\ntype Comparison struct {\n\tOp  iterator.Operator\n\tVal quad.Value\n}\n\nfunc (f Comparison) BuildIterator(qs graph.QuadStore, it iterator.Shape) iterator.Shape {\n\treturn iterator.NewComparison(it, f.Op, f.Val, qs)\n}\n\nvar _ ValueFilter = Regexp{}\n\n// Regexp filters values using regular expression.\n//\n// Since regexp patterns can not be optimized in most cases, Wildcard should be used if possible.\ntype Regexp struct {\n\tRe   *regexp.Regexp\n\tRefs bool // allow to match IRIs\n}\n\nfunc (f Regexp) BuildIterator(qs graph.QuadStore, it iterator.Shape) iterator.Shape {\n\tif f.Refs {\n\t\treturn iterator.NewRegexWithRefs(it, f.Re, qs)\n\t}\n\treturn iterator.NewRegex(it, f.Re, qs)\n}\n\nvar _ ValueFilter = Wildcard{}\n\n// Wildcard is a filter for string patterns.\n//\n//\t% - zero or more characters\n//\t? - exactly one character\ntype Wildcard struct {\n\tPattern string // allowed wildcards are: % and ?\n}\n\n// Regexp returns an analog regexp pattern in format accepted by Go stdlib (RE2).\nfunc (f Wildcard) Regexp() string {\n\tconst any = `%`\n\t// escape all meta-characters in pattern string\n\tpattern := regexp.QuoteMeta(f.Pattern)\n\t// if the pattern is anchored, add regexp analog for it\n\tif !strings.HasPrefix(pattern, any) {\n\t\tpattern = \"^\" + pattern\n\t} else {\n\t\tpattern = strings.TrimPrefix(pattern, any)\n\t}\n\tif !strings.HasSuffix(pattern, any) {\n\t\tpattern = pattern + \"$\"\n\t} else {\n\t\tpattern = strings.TrimSuffix(pattern, any)\n\t}\n\t// replace wildcards\n\tpattern = strings.NewReplacer(\n\t\tany, `.*`,\n\t\t`\\?`, `.`,\n\t).Replace(pattern)\n\treturn pattern\n}\n\nfunc (f Wildcard) BuildIterator(qs graph.QuadStore, it iterator.Shape) iterator.Shape {\n\tif f.Pattern == \"\" {\n\t\treturn iterator.NewNull()\n\t} else if strings.Trim(f.Pattern, \"%\") == \"\" {\n\t\treturn it\n\t}\n\tre, err := regexp.Compile(f.Regexp())\n\tif err != nil {\n\t\treturn iterator.NewError(err)\n\t}\n\treturn iterator.NewRegexWithRefs(it, re, qs)\n}\n\n// Count returns a count of objects in source as a single value. It always returns exactly one value.\ntype Count struct {\n\tValues Shape\n}\n\nfunc (s Count) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tvar it iterator.Shape\n\tif IsNull(s.Values) {\n\t\tit = iterator.NewNull()\n\t} else {\n\t\tit = s.Values.BuildIterator(qs)\n\t}\n\treturn iterator.NewCount(it, qs)\n}\nfunc (s Count) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.Values) {\n\t\treturn Fixed{refs.PreFetched(quad.Int(0))}, true\n\t}\n\tvar opt bool\n\ts.Values, opt = s.Values.Optimize(ctx, r)\n\tif IsNull(s.Values) {\n\t\treturn Fixed{refs.PreFetched(quad.Int(0))}, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\t// TODO: ask QS to estimate size - if it exact, then we can use it\n\treturn s, opt\n}\n\n// QuadFilter is a constraint used to filter quads that have a certain set of values on a given direction.\n// Analog of LinksTo iterator.\ntype QuadFilter struct {\n\tDir    quad.Direction\n\tValues Shape\n}\n\n// buildIterator is not exposed to force to use Quads and group filters together.\nfunc (s QuadFilter) buildIterator(qs graph.QuadStore) iterator.Shape {\n\tif s.Values == nil {\n\t\treturn iterator.NewNull()\n\t} else if v, ok := One(s.Values); ok {\n\t\treturn qs.QuadIterator(s.Dir, v)\n\t}\n\tif s.Dir == quad.Any {\n\t\tpanic(\"direction is not set\")\n\t}\n\tsub := s.Values.BuildIterator(qs)\n\treturn graph.NewLinksTo(qs, sub, s.Dir)\n}\n\n// Quads is a selector of quads with a given set of node constraints. Empty or nil Quads is equivalent to AllQuads.\n// Equivalent to And(AllQuads,LinksTo*) iterator tree.\ntype Quads []QuadFilter\n\nfunc (s *Quads) Intersect(q ...QuadFilter) {\n\t*s = append(*s, q...)\n}\nfunc (s Quads) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif len(s) == 0 {\n\t\treturn qs.QuadsAllIterator()\n\t}\n\tits := make([]iterator.Shape, 0, len(s))\n\tfor _, f := range s {\n\t\tits = append(its, f.buildIterator(qs))\n\t}\n\tif len(its) == 1 {\n\t\treturn its[0]\n\t}\n\treturn iterator.NewAnd(its...)\n}\nfunc (s Quads) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tvar opt bool\n\tsw := 0\n\trealloc := func() {\n\t\tif !opt {\n\t\t\topt = true\n\t\t\tnq := make(Quads, len(s))\n\t\t\tcopy(nq, s)\n\t\t\ts = nq\n\t\t}\n\t}\n\t// TODO: multiple constraints on the same dir -> merge as Intersect on Values of this dir\n\tfor i := 0; i < len(s); i++ {\n\t\tf := s[i]\n\t\tif f.Values == nil {\n\t\t\treturn nil, true\n\t\t}\n\t\tv, ok := f.Values.Optimize(ctx, r)\n\t\tif v == nil {\n\t\t\treturn nil, true\n\t\t}\n\t\tif ok {\n\t\t\trealloc()\n\t\t\ts[i].Values = v\n\t\t}\n\t\tswitch s[i].Values.(type) {\n\t\tcase Fixed:\n\t\t\trealloc()\n\t\t\ts[sw], s[i] = s[i], s[sw]\n\t\t\tsw++\n\t\t}\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\n// NodesFrom extracts nodes on a given direction from source quads. Similar to HasA iterator.\ntype NodesFrom struct {\n\tDir   quad.Direction\n\tQuads Shape\n}\n\nfunc (s NodesFrom) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.Quads) {\n\t\treturn iterator.NewNull()\n\t}\n\tsub := s.Quads.BuildIterator(qs)\n\tif s.Dir == quad.Any {\n\t\tpanic(\"direction is not set\")\n\t}\n\treturn graph.NewHasA(qs, sub, s.Dir)\n}\nfunc (s NodesFrom) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.Quads) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.Quads, opt = s.Quads.Optimize(ctx, r)\n\tif r != nil {\n\t\t// ignore default optimizations\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\tq, ok := s.Quads.(Quads)\n\tif !ok {\n\t\treturn s, opt\n\t}\n\t// HasA(x, LinksTo(x, y)) == y\n\tif len(q) == 1 && q[0].Dir == s.Dir {\n\t\treturn q[0].Values, true\n\t}\n\t// collect all fixed tags and push them up the tree\n\tvar (\n\t\ttags  map[string]refs.Ref\n\t\tnquad Quads\n\t)\n\tfor i, f := range q {\n\t\tif ft, ok := f.Values.(FixedTags); ok {\n\t\t\tif tags == nil {\n\t\t\t\t// allocate map and clone quad filters\n\t\t\t\ttags = make(map[string]refs.Ref)\n\t\t\t\tnquad = make([]QuadFilter, len(q))\n\t\t\t\tcopy(nquad, q)\n\t\t\t\tq = nquad\n\t\t\t}\n\t\t\tq[i].Values = ft.On\n\t\t\tfor k, v := range ft.Tags {\n\t\t\t\ttags[k] = v\n\t\t\t}\n\t\t}\n\t}\n\tif tags != nil {\n\t\t// re-run optimization without fixed tags\n\t\tns, _ := NodesFrom{Dir: s.Dir, Quads: q}.Optimize(ctx, r)\n\t\treturn FixedTags{On: ns, Tags: tags}, true\n\t}\n\tvar (\n\t\t// if quad filter contains one fixed value, it will be added to the map\n\t\tfilt map[quad.Direction]refs.Ref\n\t\t// if we see a Save from AllNodes, we will write it here, since it's a Save on quad direction\n\t\tsave map[quad.Direction][]string\n\t\t// how many filters are recognized\n\t\tn int\n\t)\n\tfor _, f := range q {\n\t\tif v, ok := One(f.Values); ok {\n\t\t\tif filt == nil {\n\t\t\t\tfilt = make(map[quad.Direction]refs.Ref)\n\t\t\t}\n\t\t\tif _, ok := filt[f.Dir]; ok {\n\t\t\t\treturn s, opt // just to be safe\n\t\t\t}\n\t\t\tfilt[f.Dir] = v\n\t\t\tn++\n\t\t} else if sv, ok := f.Values.(Save); ok {\n\t\t\tif _, ok = sv.From.(AllNodes); ok {\n\t\t\t\tif save == nil {\n\t\t\t\t\tsave = make(map[quad.Direction][]string)\n\t\t\t\t}\n\t\t\t\tsave[f.Dir] = append(save[f.Dir], sv.Tags...)\n\t\t\t\tn++\n\t\t\t}\n\t\t}\n\t}\n\tif n == len(q) {\n\t\t// if all filters were recognized we can merge this tree as a single iterator with multiple\n\t\t// constraints and multiple save commands over the same set of quads\n\t\tns, _ := QuadsAction{\n\t\t\tResult: s.Dir, // this is still a HasA, remember?\n\t\t\tFilter: filt,\n\t\t\tSave:   save,\n\t\t}.Optimize(ctx, r)\n\t\treturn ns, true\n\t}\n\t// TODO\n\treturn s, opt\n}\n\nvar _ Composite = QuadsAction{}\n\n// QuadsAction represents a set of actions that can be done to a set of quads in a single scan pass.\n// It filters quads according to Filter constraints (equivalent of LinksTo), tags directions using tags in Save field\n// and returns a specified quad direction as result of the iterator (equivalent of HasA).\n// Optionally, Size field may be set to indicate an approximate number of quads that will be returned by this query.\ntype QuadsAction struct {\n\tSize   int64 // approximate size; zero means undefined\n\tResult quad.Direction\n\tSave   map[quad.Direction][]string\n\tFilter map[quad.Direction]refs.Ref\n}\n\nfunc (s *QuadsAction) SetFilter(d quad.Direction, v refs.Ref) {\n\tif s.Filter == nil {\n\t\ts.Filter = make(map[quad.Direction]refs.Ref)\n\t}\n\ts.Filter[d] = v\n}\n\nfunc (s QuadsAction) Clone() QuadsAction {\n\tif n := len(s.Save); n != 0 {\n\t\ts2 := make(map[quad.Direction][]string, n)\n\t\tfor k, v := range s.Save {\n\t\t\ts2[k] = v\n\t\t}\n\t\ts.Save = s2\n\t} else {\n\t\ts.Save = nil\n\t}\n\tif n := len(s.Filter); n != 0 {\n\t\tf2 := make(map[quad.Direction]refs.Ref, n)\n\t\tfor k, v := range s.Filter {\n\t\t\tf2[k] = v\n\t\t}\n\t\ts.Filter = f2\n\t} else {\n\t\ts.Filter = nil\n\t}\n\treturn s\n}\nfunc (s QuadsAction) simplify() NodesFrom {\n\tq := make(Quads, 0, len(s.Save)+len(s.Filter))\n\tfor dir, val := range s.Filter {\n\t\tq = append(q, QuadFilter{Dir: dir, Values: Fixed{val}})\n\t}\n\tfor dir, tags := range s.Save {\n\t\tq = append(q, QuadFilter{Dir: dir, Values: Save{From: AllNodes{}, Tags: tags}})\n\t}\n\treturn NodesFrom{Dir: s.Result, Quads: q}\n}\nfunc (s QuadsAction) SimplifyFrom(quads Shape) Shape {\n\tq := make(Quads, 0, len(s.Save))\n\tfor dir, tags := range s.Save {\n\t\tq = append(q, QuadFilter{Dir: dir, Values: Save{From: AllNodes{}, Tags: tags}})\n\t}\n\tif len(q) != 0 {\n\t\tquads = IntersectShapes(quads, q)\n\t}\n\treturn NodesFrom{Dir: s.Result, Quads: quads}\n}\nfunc (s QuadsAction) Simplify() Shape {\n\treturn s.simplify()\n}\nfunc (s QuadsAction) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\th := s.simplify()\n\treturn h.BuildIterator(qs)\n}\nfunc (s QuadsAction) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif r != nil {\n\t\treturn r.OptimizeShape(ctx, s)\n\t}\n\t// if optimizer has stats for quad indexes we can use them to do more\n\tind, ok := r.(QuadIndexer)\n\tif !ok {\n\t\treturn s, false\n\t}\n\tif s.Size > 0 { // already optimized; specific for QuadIndexer optimization\n\t\treturn s, false\n\t}\n\tsz, exact := ind.SizeOfIndex(s.Filter)\n\tif !exact {\n\t\treturn s, false\n\t}\n\ts.Size = sz // computing size is already an optimization\n\tif sz == 0 {\n\t\t// nothing here, collapse the tree\n\t\treturn nil, true\n\t} else if sz == 1 {\n\t\t// only one quad matches this set of filters\n\t\t// try to load it from quad store, do all operations and bake result as a fixed node/tags\n\t\tif q, ok := ind.LookupQuadIndex(s.Filter); ok {\n\t\t\tfx := Fixed{q.Get(s.Result)}\n\t\t\tif len(s.Save) == 0 {\n\t\t\t\treturn fx, true\n\t\t\t}\n\t\t\tft := FixedTags{On: fx, Tags: make(map[string]refs.Ref)}\n\t\t\tfor d, tags := range s.Save {\n\t\t\t\tfor _, t := range tags {\n\t\t\t\t\tft.Tags[t] = q.Get(d)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ft, true\n\t\t}\n\t}\n\tif sz < int64(MaterializeThreshold) {\n\t\t// if this set is small enough - materialize it\n\t\treturn Materialize{Values: s, Size: int(sz)}, true\n\t}\n\treturn s, true\n}\n\n// One checks if Shape represents a single fixed value and returns it.\nfunc One(s Shape) (refs.Ref, bool) {\n\tswitch s := s.(type) {\n\tcase Fixed:\n\t\tif len(s) == 1 {\n\t\t\treturn s[0], true\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// Fixed is a static set of nodes. Defined only for a particular QuadStore.\ntype Fixed []refs.Ref\n\nfunc (s *Fixed) Add(v ...refs.Ref) {\n\t*s = append(*s, v...)\n}\nfunc (s Fixed) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tit := iterator.NewFixed()\n\tfor _, v := range s {\n\t\tif _, ok := v.(quad.Value); ok {\n\t\t\tpanic(\"quad value in fixed iterator\")\n\t\t}\n\t\tit.Add(v)\n\t}\n\treturn it\n}\nfunc (s Fixed) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif len(s) == 0 {\n\t\treturn nil, true\n\t}\n\tif r != nil {\n\t\treturn r.OptimizeShape(ctx, s)\n\t}\n\treturn s, false\n}\n\n// FixedTags adds a set of fixed tag values to query results. It does not affect query execution in any other way.\n//\n// Shape implementations should try to push these objects up the tree during optimization process.\ntype FixedTags struct {\n\tTags map[string]refs.Ref\n\tOn   Shape\n}\n\nfunc (s FixedTags) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.On) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.On.BuildIterator(qs)\n\tsv := iterator.NewSave(it)\n\tfor k, v := range s.Tags {\n\t\tsv.AddFixedTag(k, v)\n\t}\n\treturn sv\n}\nfunc (s FixedTags) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.On) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.On, opt = s.On.Optimize(ctx, r)\n\tif len(s.Tags) == 0 {\n\t\treturn s.On, true\n\t} else if s2, ok := s.On.(FixedTags); ok {\n\t\ttags := make(map[string]refs.Ref, len(s.Tags)+len(s2.Tags))\n\t\tfor k, v := range s.Tags {\n\t\t\ttags[k] = v\n\t\t}\n\t\tfor k, v := range s2.Tags {\n\t\t\ttags[k] = v\n\t\t}\n\t\ts, opt = FixedTags{On: s2.On, Tags: tags}, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\n// Lookup is a static set of values that must be resolved to nodes by QuadStore.\ntype Lookup []quad.Value\n\nfunc (s *Lookup) Add(v ...quad.Value) {\n\t*s = append(*s, v...)\n}\n\nvar _ valueResolver = graph.QuadStore(nil)\n\ntype valueResolver interface {\n\tValueOf(v quad.Value) (refs.Ref, error)\n}\n\nfunc (s Lookup) resolve(qs valueResolver) (Shape, error) {\n\t// TODO: check if QS supports batch lookup\n\tvals := make([]refs.Ref, 0, len(s))\n\tfor _, v := range s {\n\t\tgv, err := qs.ValueOf(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif gv != nil {\n\t\t\tvals = append(vals, gv)\n\t\t}\n\t}\n\tif len(vals) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn Fixed(vals), nil\n}\nfunc (s Lookup) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tf, err := s.resolve(qs)\n\tif err != nil {\n\t\treturn iterator.NewError(err)\n\t}\n\tif IsNull(f) {\n\t\treturn iterator.NewNull()\n\t}\n\treturn f.BuildIterator(qs)\n}\nfunc (s Lookup) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif r == nil {\n\t\treturn s, false\n\t}\n\tns, opt := r.OptimizeShape(ctx, s)\n\tif opt {\n\t\treturn ns, true\n\t}\n\tif qs, ok := r.(valueResolver); ok {\n\t\tres, err := s.resolve(qs)\n\t\tif err == nil {\n\t\t\tns, opt = res, true\n\t\t}\n\t}\n\treturn ns, opt\n}\n\nvar MaterializeThreshold = 100 // TODO: tune\n\n// Materialize loads results of sub-query into memory during execution to speedup iteration.\ntype Materialize struct {\n\tSize   int // approximate size; zero means undefined\n\tValues Shape\n}\n\nfunc (s Materialize) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.Values) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.Values.BuildIterator(qs)\n\treturn iterator.NewMaterializeWithSize(it, int64(s.Size))\n}\nfunc (s Materialize) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.Values) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.Values, opt = s.Values.Optimize(ctx, r)\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\nfunc clearFixedTags(arr []Shape) ([]Shape, map[string]refs.Ref) {\n\tvar tags map[string]refs.Ref\n\tfor i := 0; i < len(arr); i++ {\n\t\tif ft, ok := arr[i].(FixedTags); ok {\n\t\t\tif tags == nil {\n\t\t\t\ttags = make(map[string]refs.Ref)\n\t\t\t\tna := make([]Shape, len(arr))\n\t\t\t\tcopy(na, arr)\n\t\t\t\tarr = na\n\t\t\t}\n\t\t\tarr[i] = ft.On\n\t\t\tfor k, v := range ft.Tags {\n\t\t\t\ttags[k] = v\n\t\t\t}\n\t\t}\n\t}\n\treturn arr, tags\n}\n\n// Intersect computes an intersection of nodes between multiple queries. Similar to And iterator.\ntype Intersect []Shape\n\nfunc (s Intersect) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif len(s) == 0 {\n\t\treturn iterator.NewNull()\n\t}\n\tsub := make([]iterator.Shape, 0, len(s))\n\tfor _, c := range s {\n\t\tsub = append(sub, c.BuildIterator(qs))\n\t}\n\tif len(sub) == 1 {\n\t\treturn sub[0]\n\t}\n\treturn iterator.NewAnd(sub...)\n}\nfunc (s Intersect) Optimize(ctx context.Context, r Optimizer) (sout Shape, opt bool) {\n\tif len(s) == 0 {\n\t\treturn nil, true\n\t}\n\t// function to lazily reallocate a copy of Intersect slice\n\trealloc := func() {\n\t\tif !opt {\n\t\t\tarr := make(Intersect, len(s))\n\t\t\tcopy(arr, s)\n\t\t\ts = arr\n\t\t}\n\t}\n\t// optimize sub-iterators, return empty set if Null is found\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif IsNull(c) {\n\t\t\treturn nil, true\n\t\t}\n\t\tv, ok := c.Optimize(ctx, r)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\trealloc()\n\t\topt = true\n\t\tif IsNull(v) {\n\t\t\treturn nil, true\n\t\t}\n\t\ts[i] = v\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\tif arr, ft := clearFixedTags([]Shape(s)); ft != nil {\n\t\tns, _ := FixedTags{On: Intersect(arr), Tags: ft}.Optimize(ctx, r)\n\t\treturn ns, true\n\t}\n\tvar (\n\t\tonlyAll  = true // contains only AllNodes shapes\n\t\thasAll   = false\n\t\tfixed    []Fixed  // we will collect all Fixed, and will place it as a first iterator\n\t\ttags     []string // if we find a Save inside, we will push it outside of Intersect\n\t\tquads    Quads    // also, collect all quad filters into a single set\n\t\toptional []Shape\n\t)\n\tremove := func(i *int, optimized bool) {\n\t\trealloc()\n\t\tif optimized {\n\t\t\topt = true\n\t\t}\n\t\tv := *i\n\t\ts = append(s[:v], s[v+1:]...)\n\t\tv--\n\t\t*i = v\n\t}\n\t// second pass - remove AllNodes, merge Quads, collect Fixed, collect Save, merge Intersects\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tswitch c := c.(type) {\n\t\tcase AllNodes: // remove AllNodes - it's useless in the intersection\n\t\t\tremove(&i, true)\n\t\t\thasAll = true\n\t\t\tcontinue // prevent resetting of onlyAll\n\t\tcase Quads: // merge all quad filters\n\t\t\tremove(&i, false)\n\t\t\tif quads == nil {\n\t\t\t\tquads = c[:len(c):len(c)]\n\t\t\t} else {\n\t\t\t\topt = true\n\t\t\t\tquads = append(quads, c...)\n\t\t\t}\n\t\tcase Fixed: // collect all Fixed sets\n\t\t\tremove(&i, true)\n\t\t\tfixed = append(fixed, c)\n\t\tcase Intersect: // merge with other Intersects\n\t\t\tremove(&i, true)\n\t\t\ts = append(s, c...)\n\t\tcase IntersectOpt: // merge with IntersectOpt\n\t\t\tremove(&i, true)\n\t\t\ts = append(s, c.Sub...)\n\t\t\toptional = append(optional, c.Opt...)\n\t\tcase Save: // push Save outside of Intersect\n\t\t\trealloc()\n\t\t\topt = true\n\t\t\ttags = append(tags, c.Tags...)\n\t\t\ts[i] = c.From\n\t\t\ti--\n\t\t}\n\t\tonlyAll = false\n\t}\n\tif onlyAll {\n\t\treturn AllNodes{}, true\n\t}\n\tif len(tags) != 0 {\n\t\t// don't forget to move Save outside of Intersect at the end\n\t\tdefer func() {\n\t\t\tif IsNull(sout) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsv := Save{From: sout, Tags: tags}\n\t\t\tvar topt bool\n\t\t\tsout, topt = sv.Optimize(ctx, r)\n\t\t\topt = opt || topt\n\t\t}()\n\t}\n\tif len(optional) != 0 {\n\t\t// don't forget to add optional paths\n\t\tdefer func() {\n\t\t\tif IsNull(sout) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tout := IntersectOpt{Opt: optional}\n\t\t\tif so, ok := sout.(Intersect); ok {\n\t\t\t\tout.Sub = so\n\t\t\t} else {\n\t\t\t\tout.Sub = Intersect{sout}\n\t\t\t}\n\t\t\tvar topt bool\n\t\t\tsout, topt = out.Optimize(ctx, r)\n\t\t\topt = opt || topt\n\t\t}()\n\t}\n\tif quads != nil {\n\t\tnq, qopt := quads.Optimize(ctx, r)\n\t\tif IsNull(nq) {\n\t\t\treturn nil, true\n\t\t}\n\t\topt = opt || qopt\n\t\ts = append(s, nq)\n\t}\n\t// TODO: intersect fixed\n\tif len(fixed) == 1 {\n\t\tfix := fixed[0]\n\t\tif len(s) == 1 {\n\t\t\t// try to push fixed down the tree\n\t\t\tswitch sf := s[0].(type) {\n\t\t\tcase QuadsAction:\n\t\t\t\t// TODO: accept an array of Fixed values\n\t\t\t\tif len(fix) == 1 {\n\t\t\t\t\t// we have a single value in Fixed that is intersected with HasA tree\n\t\t\t\t\t// this means we can add a new constraint: LinksTo(HasA.Dir, fixed)\n\t\t\t\t\t// result direction of HasA will be preserved\n\t\t\t\t\tfv := fix[0]\n\t\t\t\t\tif v := sf.Filter[sf.Result]; v != nil {\n\t\t\t\t\t\t// we have the same direction set as a fixed constraint - do filtering\n\t\t\t\t\t\tif refs.ToKey(v) != refs.ToKey(fv) {\n\t\t\t\t\t\t\treturn nil, true\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn sf, true\n\t\t\t\t\t}\n\t\t\t\t\tsf = sf.Clone()\n\t\t\t\t\tsf.SetFilter(sf.Result, fv) // LinksTo(HasA.Dir, fixed)\n\t\t\t\t\tsf.Size = 0                 // re-calculate size\n\t\t\t\t\tns, _ := sf.Optimize(ctx, r)\n\t\t\t\t\treturn ns, true\n\t\t\t\t}\n\t\t\tcase NodesFrom:\n\t\t\t\tif sq, ok := sf.Quads.(Quads); ok {\n\t\t\t\t\t// an optimization above is valid for NodesFrom+Quads as well\n\t\t\t\t\t// we can add the same constraint to Quads and remove Fixed\n\t\t\t\t\tqi := -1\n\t\t\t\t\tfor i, qf := range sq {\n\t\t\t\t\t\tif qf.Dir == sf.Dir {\n\t\t\t\t\t\t\tqi = i\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif qi < 0 {\n\t\t\t\t\t\t// no filter on this direction - append\n\t\t\t\t\t\tsf.Quads = append(Quads{\n\t\t\t\t\t\t\t{Dir: sf.Dir, Values: fix},\n\t\t\t\t\t\t}, sq...)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// already have a filter on this direction - push Fixed inside it\n\t\t\t\t\t\tsq = append(Quads{}, sq...)\n\t\t\t\t\t\tsf.Quads = sq\n\t\t\t\t\t\tqf := &sq[qi]\n\t\t\t\t\t\tqf.Values = IntersectShapes(fix, qf.Values)\n\t\t\t\t\t}\n\t\t\t\t\treturn sf, true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// place fixed as a first iterator\n\t\ts = append(s, nil)\n\t\tcopy(s[1:], s)\n\t\ts[0] = fix\n\t} else if len(fixed) > 1 {\n\t\tns := make(Intersect, len(s)+len(fixed))\n\t\tfor i, f := range fixed {\n\t\t\tns[i] = f\n\t\t}\n\t\tcopy(ns[len(fixed):], s)\n\t\ts = ns\n\t}\n\tif len(s) == 0 {\n\t\tif hasAll {\n\t\t\treturn AllNodes{}, true\n\t\t}\n\t\treturn nil, true\n\t} else if len(s) == 1 {\n\t\treturn s[0], true\n\t}\n\t// TODO: optimize order\n\treturn s, opt\n}\n\n// IntersectOpt is like Intersect but it also joins optional query shapes to the main query.\ntype IntersectOpt struct {\n\tSub Intersect\n\tOpt []Shape\n}\n\nfunc (s *IntersectOpt) Add(arr ...Shape) {\n\ts.Sub = append(s.Sub, arr...)\n}\n\nfunc (s *IntersectOpt) AddOptional(arr ...Shape) {\n\ts.Opt = append(s.Opt, arr...)\n}\n\nfunc (s IntersectOpt) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif len(s.Sub) == 0 && len(s.Opt) == 0 {\n\t\treturn iterator.NewNull()\n\t}\n\tif len(s.Sub) == 0 {\n\t\tif len(s.Opt) == 0 {\n\t\t\treturn iterator.NewNull()\n\t\t}\n\t\ts.Sub = Intersect{AllNodes{}}\n\t}\n\tsub := make([]iterator.Shape, 0, len(s.Sub))\n\topt := make([]iterator.Shape, 0, len(s.Opt))\n\tfor _, c := range s.Sub {\n\t\tsub = append(sub, c.BuildIterator(qs))\n\t}\n\tfor _, c := range s.Opt {\n\t\topt = append(opt, c.BuildIterator(qs))\n\t}\n\tif len(sub) == 1 && len(opt) == 0 {\n\t\treturn sub[0]\n\t}\n\tit := iterator.NewAnd(sub...)\n\tfor _, sit := range opt {\n\t\tit.AddOptionalIterator(sit)\n\t}\n\treturn it\n}\n\nfunc (s IntersectOpt) Optimize(ctx context.Context, r Optimizer) (_ Shape, opt bool) {\n\t// optimize optional shapes first, reallocate if necessary\n\tnewSlice := false\n\trealloc := func() {\n\t\topt = true\n\t\tif newSlice {\n\t\t\treturn\n\t\t}\n\t\tnewSlice = true\n\t\ts.Opt = append([]Shape{}, s.Opt...)\n\t}\n\tfor i := 0; i < len(s.Opt); i++ {\n\t\to := s.Opt[i]\n\t\tif IsNull(o) {\n\t\t\trealloc()\n\t\t\ts.Opt = append(s.Opt[:i], s.Opt[i+1:]...)\n\t\t\ti--\n\t\t\tcontinue\n\t\t}\n\t\to, opt2 := o.Optimize(ctx, r)\n\t\tif !opt2 {\n\t\t\tcontinue\n\t\t}\n\t\trealloc()\n\t\tif IsNull(o) {\n\t\t\ts.Opt = append(s.Opt[:i], s.Opt[i+1:]...)\n\t\t\ti--\n\t\t} else {\n\t\t\ts.Opt[i] = o\n\t\t}\n\t}\n\tif len(s.Opt) == 0 {\n\t\t// no optional - replace with a regular intersection\n\t\tsi, _ := s.Sub.Optimize(ctx, r)\n\t\treturn si, true\n\t}\n\tif len(s.Sub) == 0 {\n\t\t// force at least All to be in the intersection\n\t\ts.Sub = Intersect{AllNodes{}}\n\t\topt = true\n\t} else {\n\t\tsub, opt2 := s.Sub.Optimize(ctx, r)\n\t\tif IsNull(sub) {\n\t\t\treturn nil, true\n\t\t}\n\t\topt = opt || opt2\n\t\tswitch sub := sub.(type) {\n\t\tcase Intersect:\n\t\t\ts.Sub = sub\n\t\tcase IntersectOpt:\n\t\t\ts = sub\n\t\t\topt = true\n\t\tdefault:\n\t\t\ts.Sub = Intersect{sub}\n\t\t\topt = true\n\t\t}\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\n// Union joins results of multiple queries together. It does not make results unique.\ntype Union []Shape\n\nfunc (s Union) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif len(s) == 0 {\n\t\treturn iterator.NewNull()\n\t}\n\tsub := make([]iterator.Shape, 0, len(s))\n\tfor _, c := range s {\n\t\tsub = append(sub, c.BuildIterator(qs))\n\t}\n\tif len(sub) == 1 {\n\t\treturn sub[0]\n\t}\n\treturn iterator.NewOr(sub...)\n}\nfunc (s Union) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tvar opt bool\n\trealloc := func() {\n\t\tif !opt {\n\t\t\tarr := make(Union, len(s))\n\t\t\tcopy(arr, s)\n\t\t\ts = arr\n\t\t}\n\t}\n\t// optimize subiterators\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif c == nil {\n\t\t\tcontinue\n\t\t}\n\t\tv, ok := c.Optimize(ctx, r)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\trealloc()\n\t\topt = true\n\t\ts[i] = v\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\tif arr, ft := clearFixedTags([]Shape(s)); ft != nil {\n\t\tns, _ := FixedTags{On: Union(arr), Tags: ft}.Optimize(ctx, r)\n\t\treturn ns, true\n\t}\n\t// second pass - remove Null\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif IsNull(c) {\n\t\t\trealloc()\n\t\t\topt = true\n\t\t\ts = append(s[:i], s[i+1:]...)\n\t\t}\n\t}\n\tif len(s) == 0 {\n\t\treturn nil, true\n\t} else if len(s) == 1 {\n\t\treturn s[0], true\n\t}\n\t// TODO: join Fixed\n\treturn s, opt\n}\n\n// Page provides a simple form of pagination. Can be used to skip or limit results.\ntype Page struct {\n\tFrom  Shape\n\tSkip  int64\n\tLimit int64 // zero means unlimited\n}\n\nfunc (s Page) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.From) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.From.BuildIterator(qs)\n\tif s.Skip > 0 {\n\t\tit = iterator.NewSkip(it, s.Skip)\n\t}\n\tif s.Limit > 0 {\n\t\tit = iterator.NewLimit(it, s.Limit)\n\t}\n\treturn it\n}\nfunc (s Page) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.From, opt = s.From.Optimize(ctx, r)\n\tif s.Skip <= 0 && s.Limit <= 0 {\n\t\treturn s.From, true\n\t}\n\tif p, ok := s.From.(Page); ok {\n\t\tp2 := p.ApplyPage(s)\n\t\tif p2 == nil {\n\t\t\treturn nil, true\n\t\t}\n\t\ts, opt = *p2, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\t// TODO: check size\n\treturn s, opt\n}\nfunc (s Page) ApplyPage(p Page) *Page {\n\ts.Skip += p.Skip\n\tif s.Limit > 0 {\n\t\ts.Limit -= p.Skip\n\t\tif s.Limit <= 0 {\n\t\t\treturn nil\n\t\t}\n\t\tif p.Limit > 0 && s.Limit > p.Limit {\n\t\t\ts.Limit = p.Limit\n\t\t}\n\t} else {\n\t\ts.Limit = p.Limit\n\t}\n\treturn &s\n}\n\n// Unique makes query results unique.\ntype Unique struct {\n\tFrom Shape\n}\n\nfunc (s Unique) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.From) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.From.BuildIterator(qs)\n\treturn iterator.NewUnique(it)\n}\nfunc (s Unique) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.From, opt = s.From.Optimize(ctx, r)\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\n// Save tags a results of query with provided tags.\ntype Save struct {\n\tTags []string\n\tFrom Shape\n}\n\nfunc (s Save) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.From) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.From.BuildIterator(qs)\n\tif len(s.Tags) != 0 {\n\t\treturn iterator.NewSave(it, s.Tags...)\n\t}\n\treturn it\n}\nfunc (s Save) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.From, opt = s.From.Optimize(ctx, r)\n\tif len(s.Tags) == 0 {\n\t\treturn s.From, true\n\t} else if IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n\nfunc FilterQuads(subject, predicate, object, label []quad.Value) Shape {\n\tvar q Quads\n\tif len(subject) != 0 {\n\t\tq = append(q, QuadFilter{Dir: quad.Subject, Values: Lookup(subject)})\n\t}\n\tif len(predicate) != 0 {\n\t\tq = append(q, QuadFilter{Dir: quad.Predicate, Values: Lookup(predicate)})\n\t}\n\tif len(object) != 0 {\n\t\tq = append(q, QuadFilter{Dir: quad.Object, Values: Lookup(object)})\n\t}\n\tif len(label) != 0 {\n\t\tq = append(q, QuadFilter{Dir: quad.Label, Values: Lookup(label)})\n\t}\n\treturn q\n}\n\ntype Sort struct {\n\tFrom Shape\n}\n\nfunc (s Sort) BuildIterator(qs graph.QuadStore) iterator.Shape {\n\tif IsNull(s.From) {\n\t\treturn iterator.NewNull()\n\t}\n\tit := s.From.BuildIterator(qs)\n\treturn iterator.NewSort(qs, it)\n}\nfunc (s Sort) Optimize(ctx context.Context, r Optimizer) (Shape, bool) {\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tvar opt bool\n\ts.From, opt = s.From.Optimize(ctx, r)\n\tif IsNull(s.From) {\n\t\treturn nil, true\n\t}\n\tif r != nil {\n\t\tns, nopt := r.OptimizeShape(ctx, s)\n\t\treturn ns, opt || nopt\n\t}\n\treturn s, opt\n}\n"
  },
  {
    "path": "query/shape/shape_test.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage shape_test\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/graphmock\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/refs\"\n\t. \"github.com/cayleygraph/cayley/query/shape\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc intVal(v int) refs.Ref {\n\treturn graphmock.IntVal(v)\n}\n\nvar _ Optimizer = ValLookup(nil)\nvar _ graph.QuadStore = ValLookup(nil)\n\ntype ValLookup map[quad.Value]refs.Ref\n\nfunc (qs ValLookup) OptimizeShape(ctx context.Context, s Shape) (Shape, bool) {\n\treturn s, false // emulate dumb quad store\n}\nfunc (qs ValLookup) ValueOf(v quad.Value) (refs.Ref, error) {\n\treturn qs[v], nil\n}\n\nfunc (ValLookup) NewQuadWriter() (quad.WriteCloser, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) ApplyDeltas(_ []graph.Delta, _ graph.IgnoreOpts) error {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) Quad(_ refs.Ref) (quad.Quad, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) QuadIterator(_ quad.Direction, _ refs.Ref) iterator.Shape {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) QuadIteratorSize(ctx context.Context, d quad.Direction, val refs.Ref) (refs.Size, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) NodesAllIterator() iterator.Shape {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) QuadsAllIterator() iterator.Shape {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) NameOf(_ refs.Ref) (quad.Value, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) Stats(ctx context.Context, exact bool) (graph.Stats, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) Close() error {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) QuadDirection(_ refs.Ref, _ quad.Direction) (refs.Ref, error) {\n\tpanic(\"not implemented\")\n}\nfunc (ValLookup) Type() string {\n\tpanic(\"not implemented\")\n}\n\nfunc emptySet() Shape {\n\treturn NodesFrom{\n\t\tDir: quad.Predicate,\n\t\tQuads: Intersect{Quads{\n\t\t\t{Dir: quad.Object,\n\t\t\t\tValues: Lookup{quad.IRI(\"not-existent\")},\n\t\t\t},\n\t\t}},\n\t}\n}\n\nvar optimizeCases = []struct {\n\tname   string\n\tfrom   Shape\n\texpect Shape\n\topt    bool\n\tqs     ValLookup\n}{\n\t{\n\t\tname:   \"all\",\n\t\tfrom:   AllNodes{},\n\t\topt:    false,\n\t\texpect: AllNodes{},\n\t},\n\t{\n\t\tname: \"page min limit\",\n\t\tfrom: Page{\n\t\t\tLimit: 5,\n\t\t\tFrom: Page{\n\t\t\t\tLimit: 3,\n\t\t\t\tFrom:  AllNodes{},\n\t\t\t},\n\t\t},\n\t\topt: true,\n\t\texpect: Page{\n\t\t\tLimit: 3,\n\t\t\tFrom:  AllNodes{},\n\t\t},\n\t},\n\t{\n\t\tname: \"page skip and limit\",\n\t\tfrom: Page{\n\t\t\tSkip: 3, Limit: 3,\n\t\t\tFrom: Page{\n\t\t\t\tSkip: 2, Limit: 5,\n\t\t\t\tFrom: AllNodes{},\n\t\t\t},\n\t\t},\n\t\topt: true,\n\t\texpect: Page{\n\t\t\tSkip: 5, Limit: 2,\n\t\t\tFrom: AllNodes{},\n\t\t},\n\t},\n\t{\n\t\tname:   \"intersect tagged all\",\n\t\tfrom:   Intersect{Save{Tags: []string{\"id\"}, From: AllNodes{}}},\n\t\topt:    true,\n\t\texpect: Save{Tags: []string{\"id\"}, From: AllNodes{}},\n\t},\n\t{\n\t\tname: \"intersect quads and lookup resolution\",\n\t\tfrom: Intersect{\n\t\t\tQuads{\n\t\t\t\t{Dir: quad.Subject, Values: Lookup{quad.IRI(\"bob\")}},\n\t\t\t},\n\t\t\tQuads{\n\t\t\t\t{Dir: quad.Object, Values: Lookup{quad.IRI(\"alice\")}},\n\t\t\t},\n\t\t},\n\t\topt: true,\n\t\texpect: Quads{\n\t\t\t{Dir: quad.Subject, Values: Fixed{intVal(1)}},\n\t\t\t{Dir: quad.Object, Values: Fixed{intVal(2)}},\n\t\t},\n\t\tqs: ValLookup{\n\t\t\tquad.IRI(\"bob\"):   intVal(1),\n\t\t\tquad.IRI(\"alice\"): intVal(2),\n\t\t},\n\t},\n\t{\n\t\tname: \"intersect nodes, remove all, join intersects\",\n\t\tfrom: Intersect{\n\t\t\tAllNodes{},\n\t\t\tNodesFrom{Dir: quad.Subject, Quads: Quads{}},\n\t\t\tIntersect{\n\t\t\t\tLookup{quad.IRI(\"alice\")},\n\t\t\t\tUnique{NodesFrom{Dir: quad.Object, Quads: Quads{}}},\n\t\t\t},\n\t\t},\n\t\topt: true,\n\t\texpect: Intersect{\n\t\t\tFixed{intVal(1)},\n\t\t\tQuadsAction{Result: quad.Subject},\n\t\t\tUnique{QuadsAction{Result: quad.Object}},\n\t\t},\n\t\tqs: ValLookup{\n\t\t\tquad.IRI(\"alice\"): intVal(1),\n\t\t},\n\t},\n\t{\n\t\tname: \"push Save out of intersect\",\n\t\tfrom: Intersect{\n\t\t\tSave{\n\t\t\t\tTags: []string{\"id\"},\n\t\t\t\tFrom: NodesFrom{Dir: quad.Subject, Quads: Quads{}},\n\t\t\t},\n\t\t\tUnique{NodesFrom{Dir: quad.Object, Quads: Quads{}}},\n\t\t},\n\t\topt: true,\n\t\texpect: Save{\n\t\t\tTags: []string{\"id\"},\n\t\t\tFrom: Intersect{\n\t\t\t\tQuadsAction{Result: quad.Subject},\n\t\t\t\tUnique{QuadsAction{Result: quad.Object}},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\tname: \"collapse empty set\",\n\t\tfrom: Intersect{Quads{\n\t\t\t{Dir: quad.Subject, Values: Union{\n\t\t\t\tUnique{emptySet()},\n\t\t\t}},\n\t\t}},\n\t\topt:    true,\n\t\texpect: Null{},\n\t},\n\t{ // remove \"all nodes\" in intersect, merge Fixed and order them first\n\t\tname: \"remove all in intersect and reorder\",\n\t\tfrom: Intersect{\n\t\t\tAllNodes{},\n\t\t\tFixed{intVal(1), intVal(2)},\n\t\t\tSave{From: AllNodes{}, Tags: []string{\"all\"}},\n\t\t\tFixed{intVal(2)},\n\t\t},\n\t\topt: true,\n\t\texpect: Save{\n\t\t\tFrom: Intersect{\n\t\t\t\tFixed{intVal(1), intVal(2)},\n\t\t\t\tFixed{intVal(2)},\n\t\t\t},\n\t\t\tTags: []string{\"all\"},\n\t\t},\n\t},\n\t{\n\t\tname: \"remove HasA-LinksTo pairs\",\n\t\tfrom: NodesFrom{\n\t\t\tDir: quad.Subject,\n\t\t\tQuads: Quads{{\n\t\t\t\tDir:    quad.Subject,\n\t\t\t\tValues: Fixed{intVal(1)},\n\t\t\t}},\n\t\t},\n\t\topt:    true,\n\t\texpect: Fixed{intVal(1)},\n\t},\n\t{ // pop fixed tags to the top of the tree\n\t\tname: \"pop fixed tags\",\n\t\tfrom: NodesFrom{Dir: quad.Subject, Quads: Quads{\n\t\t\tQuadFilter{Dir: quad.Predicate, Values: Intersect{\n\t\t\t\tFixedTags{\n\t\t\t\t\tTags: map[string]refs.Ref{\"foo\": intVal(1)},\n\t\t\t\t\tOn: NodesFrom{Dir: quad.Subject,\n\t\t\t\t\t\tQuads: Quads{\n\t\t\t\t\t\t\tQuadFilter{Dir: quad.Object, Values: FixedTags{\n\t\t\t\t\t\t\t\tTags: map[string]refs.Ref{\"bar\": intVal(2)},\n\t\t\t\t\t\t\t\tOn:   Fixed{intVal(3)},\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}},\n\t\t}},\n\t\topt: true,\n\t\texpect: FixedTags{\n\t\t\tTags: map[string]refs.Ref{\"foo\": intVal(1), \"bar\": intVal(2)},\n\t\t\tOn: NodesFrom{Dir: quad.Subject, Quads: Quads{\n\t\t\t\tQuadFilter{Dir: quad.Predicate, Values: QuadsAction{\n\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\tFilter: map[quad.Direction]refs.Ref{quad.Object: intVal(3)},\n\t\t\t\t}},\n\t\t\t}},\n\t\t},\n\t},\n\t{ // remove optional empty set from intersect\n\t\tname: \"remove optional empty set\",\n\t\tfrom: IntersectOpt{\n\t\t\tSub: Intersect{\n\t\t\t\tAllNodes{},\n\t\t\t\tSave{From: AllNodes{}, Tags: []string{\"all\"}},\n\t\t\t\tFixed{intVal(2)},\n\t\t\t},\n\t\t\tOpt: []Shape{Save{\n\t\t\t\tFrom: emptySet(),\n\t\t\t\tTags: []string{\"name\"},\n\t\t\t}},\n\t\t},\n\t\topt: true,\n\t\texpect: Save{\n\t\t\tFrom: Fixed{intVal(2)},\n\t\t\tTags: []string{\"all\"},\n\t\t},\n\t},\n\t{ // push fixed node from intersect into nodes.quads\n\t\tname: \"push fixed into nodes.quads\",\n\t\tfrom: Intersect{\n\t\t\tFixed{intVal(1)},\n\t\t\tNodesFrom{\n\t\t\t\tDir: quad.Subject,\n\t\t\t\tQuads: Quads{\n\t\t\t\t\t{Dir: quad.Predicate, Values: Fixed{intVal(2)}},\n\t\t\t\t\t{\n\t\t\t\t\t\tDir: quad.Object,\n\t\t\t\t\t\tValues: NodesFrom{\n\t\t\t\t\t\t\tDir: quad.Subject,\n\t\t\t\t\t\t\tQuads: Quads{\n\t\t\t\t\t\t\t\t{Dir: quad.Predicate, Values: Fixed{intVal(2)}},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\topt: true,\n\t\texpect: NodesFrom{\n\t\t\tDir: quad.Subject,\n\t\t\tQuads: Quads{\n\t\t\t\t{Dir: quad.Subject, Values: Fixed{intVal(1)}},\n\t\t\t\t{Dir: quad.Predicate, Values: Fixed{intVal(2)}},\n\t\t\t\t{\n\t\t\t\t\tDir: quad.Object,\n\t\t\t\t\tValues: QuadsAction{\n\t\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\t\tFilter: map[quad.Direction]refs.Ref{\n\t\t\t\t\t\t\tquad.Predicate: intVal(2),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\tname: \"all optional\",\n\t\tfrom: Intersect{IntersectOpt{\n\t\t\tSub: Intersect{\n\t\t\t\tSave{Tags: []string{\"id\"}, From: AllNodes{}},\n\t\t\t},\n\t\t\tOpt: []Shape{\n\t\t\t\tNodesFrom{Dir: quad.Subject, Quads: Quads{\n\t\t\t\t\tQuadFilter{Dir: quad.Object, Values: Save{Tags: []string{\"status\"}, From: AllNodes{}}},\n\t\t\t\t\tQuadFilter{Dir: quad.Predicate, Values: Fixed{intVal(1)}},\n\t\t\t\t}},\n\t\t\t},\n\t\t}},\n\t\topt: true,\n\t\texpect: Save{\n\t\t\tTags: []string{\"id\"},\n\t\t\tFrom: IntersectOpt{\n\t\t\t\tSub: Intersect{AllNodes{}},\n\t\t\t\tOpt: []Shape{\n\t\t\t\t\tQuadsAction{Result: quad.Subject,\n\t\t\t\t\t\tSave:   map[quad.Direction][]string{quad.Object: {\"status\"}},\n\t\t\t\t\t\tFilter: map[quad.Direction]refs.Ref{quad.Predicate: intVal(1)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc TestOptimize(t *testing.T) {\n\tfor _, c := range optimizeCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tqs := c.qs\n\t\t\tgot, opt := Optimize(context.TODO(), c.from, qs)\n\t\t\tassert.Equal(t, c.expect, got)\n\t\t\tassert.Equal(t, c.opt, opt)\n\t\t})\n\t}\n}\n\nfunc TestWalk(t *testing.T) {\n\tvar s Shape = NodesFrom{\n\t\tDir: quad.Subject,\n\t\tQuads: Quads{\n\t\t\t{Dir: quad.Subject, Values: Fixed{intVal(1)}},\n\t\t\t{Dir: quad.Predicate, Values: Fixed{intVal(2)}},\n\t\t\t{\n\t\t\t\tDir: quad.Object,\n\t\t\t\tValues: QuadsAction{\n\t\t\t\t\tResult: quad.Subject,\n\t\t\t\t\tFilter: map[quad.Direction]refs.Ref{\n\t\t\t\t\t\tquad.Predicate: intVal(2),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tvar types []string\n\tWalk(s, func(s Shape) bool {\n\t\ttypes = append(types, reflect.TypeOf(s).String())\n\t\treturn true\n\t})\n\trequire.Equal(t, []string{\n\t\t\"shape.NodesFrom\",\n\t\t\"shape.Quads\",\n\t\t\"shape.Fixed\",\n\t\t\"shape.Fixed\",\n\t\t\"shape.QuadsAction\",\n\t}, types)\n}\n"
  },
  {
    "path": "schema/loader.go",
    "content": "package schema\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n)\n\nvar (\n\terrNotFound               = errors.New(\"not found\")\n\terrRequiredFieldIsMissing = errors.New(\"required field is missing\")\n)\n\n// Optimize flags controls an optimization step performed before queries.\nvar Optimize = true\n\n// IsNotFound check if error is related to a missing object (either because of wrong ID or because of type constrains).\nfunc IsNotFound(err error) bool {\n\treturn err == errNotFound || err == errRequiredFieldIsMissing\n}\n\n// LoadTo will load a sub-graph of objects starting from ids (or from any nodes, if empty)\n// to a destination Go object. Destination can be a struct, slice or channel.\n//\n// Mapping to quads is done via Go struct tag \"quad\" or \"json\" as a fallback.\n//\n// A simplest mapping is an \"@id\" tag which saves node ID (subject of a quad) into tagged field.\n//\n//\ttype Node struct{\n//\t\tID quad.IRI `json:\"@id\"` // or `quad:\"@id\"`\n// \t}\n//\n// Field with an \"@id\" tag is omitted, but in case of Go->quads mapping new ID will be generated\n// using GenerateID callback, which can be changed to provide a custom mappings.\n//\n// All other tags are interpreted as a predicate name for a specific field:\n//\n//\ttype Person struct{\n//\t\tID quad.IRI `json:\"@id\"`\n//\t\tName string `json:\"name\"`\n// \t}\n//\tp := Person{\"bob\",\"Bob\"}\n//\t// is equivalent to triple:\n//\t// <bob> <name> \"Bob\"\n//\n// Predicate IRIs in RDF can have a long namespaces, but they can be written in short\n// form. They will be expanded automatically if namespace prefix is registered within\n// QuadStore or globally via \"voc\" package.\n// There is also a special predicate name \"@type\" which is mapped to \"rdf:type\" IRI.\n//\n//\tvoc.RegisterPrefix(\"ex:\", \"http://example.org/\")\n//\ttype Person struct{\n//\t\tID quad.IRI `json:\"@id\"`\n//\t\tType quad.IRI `json:\"@type\"`\n//\t\tName string `json:\"ex:name\"` // will be expanded to http://example.org/name\n// \t}\n//\tp := Person{\"bob\",quad.IRI(\"Person\"),\"Bob\"}\n//\t// is equivalent to triples:\n//\t// <bob> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <Person>\n//\t// <bob> <http://example.org/name> \"Bob\"\n//\n// Predicate link direction can be reversed with a special tag syntax (not available for \"json\" tag):\n//\n// \ttype Person struct{\n//\t\tID quad.IRI `json:\"@id\"`\n//\t\tName string `json:\"name\"` // same as `quad:\"name\"` or `quad:\"name > *\"`\n//\t\tParents []quad.IRI `quad:\"isParentOf < *\"`\n// \t}\n//\tp := Person{\"bob\",\"Bob\",[]quad.IRI{\"alice\",\"fred\"}}\n//\t// is equivalent to triples:\n//\t// <bob> <name> \"Bob\"\n//\t// <alice> <isParentOf> <bob>\n//\t// <fred> <isParentOf> <bob>\n//\n// All fields in structs are interpreted as required (except slices), thus struct will not be\n// loaded if one of fields is missing. An \"optional\" tag can be specified to relax this requirement.\n// Also, \"required\" can be specified for slices to alter default value.\n//\n//\ttype Person struct{\n//\t\tID quad.IRI `json:\"@id\"`\n//\t\tName string `json:\"name\"` // required field\n//\t\tThirdName string `quad:\"thirdName,optional\"` // can be empty\n//\t\tFollowedBy []quad.IRI `quad:\"follows\"`\n// \t}\nfunc (c *Config) LoadTo(ctx context.Context, qs graph.QuadStore, dst interface{}, ids ...quad.Value) error {\n\treturn c.LoadToDepth(ctx, qs, dst, -1, ids...)\n}\n\n// LoadToDepth is the same as LoadTo, but stops at a specified depth.\n// Negative value means unlimited depth, and zero means top level only.\nfunc (c *Config) LoadToDepth(ctx context.Context, qs graph.QuadStore, dst interface{}, depth int, ids ...quad.Value) error {\n\tif dst == nil {\n\t\treturn fmt.Errorf(\"nil destination object\")\n\t}\n\tvar it iterator.Shape\n\tif len(ids) != 0 {\n\t\tfixed := iterator.NewFixed()\n\t\tfor _, id := range ids {\n\t\t\tidv, err := qs.ValueOf(id)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfixed.Add(idv)\n\t\t}\n\t\tit = fixed\n\t}\n\tvar rv reflect.Value\n\tif v, ok := dst.(reflect.Value); ok {\n\t\trv = v\n\t} else {\n\t\trv = reflect.ValueOf(dst)\n\t}\n\treturn c.LoadIteratorToDepth(ctx, qs, rv, depth, it)\n}\n\n// LoadPathTo is the same as LoadTo, but starts loading objects from a given path.\nfunc (c *Config) LoadPathTo(ctx context.Context, qs graph.QuadStore, dst interface{}, p *path.Path) error {\n\treturn c.LoadIteratorTo(ctx, qs, reflect.ValueOf(dst), p.BuildIterator(ctx))\n}\n\n// LoadIteratorTo is a lower level version of LoadTo.\n//\n// It expects an iterator of nodes to be passed explicitly and\n// destination value to be obtained via reflect package manually.\n//\n// Nodes iterator can be nil, All iterator will be used in this case.\nfunc (c *Config) LoadIteratorTo(ctx context.Context, qs graph.QuadStore, dst reflect.Value, list iterator.Shape) error {\n\treturn c.LoadIteratorToDepth(ctx, qs, dst, -1, list)\n}\n\n// LoadIteratorToDepth is the same as LoadIteratorTo, but stops at a specified depth.\n// Negative value means unlimited depth, and zero means top level only.\nfunc (c *Config) LoadIteratorToDepth(ctx context.Context, qs graph.QuadStore, dst reflect.Value, depth int, list iterator.Shape) error {\n\tif depth >= 0 {\n\t\t// 0 depth means \"current level only\" for user, but it's easier to make depth=0 a stop condition\n\t\tdepth++\n\t}\n\tl := c.newLoader(qs)\n\treturn l.loadIteratorToDepth(ctx, dst, depth, list)\n}\n\ntype loader struct {\n\tc  *Config\n\tqs graph.QuadStore\n\n\tpathForType     map[reflect.Type]*path.Path\n\tpathForTypeRoot map[reflect.Type]*path.Path\n\n\tseen map[quad.Value]reflect.Value\n}\n\nfunc (c *Config) newLoader(qs graph.QuadStore) *loader {\n\treturn &loader{\n\t\tc:  c,\n\t\tqs: qs,\n\n\t\tpathForType:     make(map[reflect.Type]*path.Path),\n\t\tpathForTypeRoot: make(map[reflect.Type]*path.Path),\n\n\t\tseen: make(map[quad.Value]reflect.Value),\n\t}\n}\n\nfunc (l *loader) makePathForType(rt reflect.Type, tagPref string, rootOnly bool) (*path.Path, error) {\n\tfor rt.Kind() == reflect.Ptr {\n\t\trt = rt.Elem()\n\t}\n\tif rt.Kind() != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"expected struct, got %v\", rt)\n\t}\n\tif tagPref == \"\" {\n\t\tm := l.pathForType\n\t\tif rootOnly {\n\t\t\tm = l.pathForTypeRoot\n\t\t}\n\t\tif p, ok := m[rt]; ok {\n\t\t\treturn p, nil\n\t\t}\n\t}\n\n\tp := path.StartMorphism()\n\n\tif iri := getTypeIRI(rt); iri != quad.IRI(\"\") {\n\t\tp = p.Has(l.c.iri(iriType), iri)\n\t}\n\n\t// TODO(dennwc): rewrite to shapes\n\n\tallOptional := true\n\tvar alt *path.Path\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tf := rt.Field(i)\n\t\tif f.Anonymous {\n\t\t\tpa, err := l.makePathForType(f.Type, tagPref+f.Name+\".\", rootOnly)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tp = p.Follow(pa)\n\t\t\tcontinue\n\t\t}\n\t\tname := f.Name\n\t\trule, err := l.c.fieldRule(f)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if rule == nil { // skip\n\t\t\tcontinue\n\t\t}\n\t\tft := f.Type\n\t\tif ft.Kind() == reflect.Ptr {\n\t\t\tft = ft.Elem()\n\t\t}\n\t\tif err = checkFieldType(ft); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tswitch rule := rule.(type) {\n\t\tcase idRule:\n\t\t\tp = p.Tag(tagPref + name)\n\t\tcase constraintRule:\n\t\t\tallOptional = false\n\t\t\tvar nodes []quad.Value\n\t\t\tif rule.Val != \"\" {\n\t\t\t\tnodes = []quad.Value{rule.Val}\n\t\t\t}\n\t\t\tif rule.Rev {\n\t\t\t\tp = p.HasReverse(rule.Pred, nodes...)\n\t\t\t} else {\n\t\t\t\tp = p.Has(rule.Pred, nodes...)\n\t\t\t}\n\t\tcase saveRule:\n\t\t\ttag := tagPref + name\n\t\t\tif rule.Opt {\n\t\t\t\tif !rootOnly {\n\t\t\t\t\tif rule.Rev {\n\t\t\t\t\t\tp = p.SaveOptionalReverse(rule.Pred, tag)\n\t\t\t\t\t\tif allOptional {\n\t\t\t\t\t\t\tap := path.StartMorphism().HasReverse(rule.Pred)\n\t\t\t\t\t\t\tif alt == nil {\n\t\t\t\t\t\t\t\talt = ap\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\talt = alt.Or(ap)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tp = p.SaveOptional(rule.Pred, tag)\n\t\t\t\t\t\tif allOptional {\n\t\t\t\t\t\t\tap := path.StartMorphism().Has(rule.Pred)\n\t\t\t\t\t\t\tif alt == nil {\n\t\t\t\t\t\t\t\talt = ap\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\talt = alt.Or(ap)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if rootOnly { // do not save field, enforce constraint only\n\t\t\t\tallOptional = false\n\t\t\t\tif rule.Rev {\n\t\t\t\t\tp = p.HasReverse(rule.Pred)\n\t\t\t\t} else {\n\t\t\t\t\tp = p.Has(rule.Pred)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tallOptional = false\n\t\t\t\tif rule.Rev {\n\t\t\t\t\tp = p.SaveReverse(rule.Pred, tag)\n\t\t\t\t} else {\n\t\t\t\t\tp = p.Save(rule.Pred, tag)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif allOptional {\n\t\tp = p.And(alt.Unique())\n\t}\n\tif tagPref != \"\" {\n\t\treturn p, nil\n\t}\n\tm := l.pathForType\n\tif rootOnly {\n\t\tm = l.pathForTypeRoot\n\t}\n\tm[rt] = p\n\treturn p, nil\n}\n\nfunc (l *loader) loadToValue(ctx context.Context, dst reflect.Value, depth int, m map[string][]graph.Ref, tagPref string) error {\n\tif ctx == nil {\n\t\tctx = context.TODO()\n\t}\n\tfor dst.Kind() == reflect.Ptr {\n\t\tdst = dst.Elem()\n\t}\n\trt := dst.Type()\n\tif rt.Kind() != reflect.Struct {\n\t\treturn fmt.Errorf(\"expected struct, got %v\", rt)\n\t}\n\tvar fields fieldRules\n\tif v := ctx.Value(fieldsCtxKey{}); v != nil {\n\t\tfields = v.(fieldRules)\n\t} else {\n\t\tnfields, err := l.c.rulesFor(rt)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfields = nfields\n\t}\n\tif depth != 0 { // do not check required fields if depth limit is reached\n\t\tfor name, field := range fields {\n\t\t\tif r, ok := field.(saveRule); ok && !r.Opt {\n\t\t\t\tif vals := m[name]; len(vals) == 0 {\n\t\t\t\t\treturn errRequiredFieldIsMissing\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t}\n\t\tf := rt.Field(i)\n\t\tname := f.Name\n\t\tif err := checkFieldType(f.Type); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdf := dst.Field(i)\n\t\tif f.Anonymous {\n\t\t\tif err := l.loadToValue(ctx, df, depth, m, tagPref+name+\".\"); err != nil {\n\t\t\t\treturn fmt.Errorf(\"load anonymous field %s failed: %v\", f.Name, err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\trules := fields[tagPref+name]\n\t\tif rules == nil {\n\t\t\tcontinue\n\t\t}\n\t\tarr, ok := m[tagPref+name]\n\t\tif !ok || len(arr) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tft := f.Type\n\t\tnative := isNative(ft)\n\t\tptr := ft.Kind() == reflect.Ptr\n\t\tfor ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice {\n\t\t\tft = ft.Elem()\n\t\t\tnative = native || isNative(ft)\n\t\t\tswitch ft.Kind() {\n\t\t\tcase reflect.Ptr:\n\t\t\t\tptr = true\n\t\t\tcase reflect.Slice:\n\t\t\t\tptr = false\n\t\t\t}\n\t\t}\n\t\trecursive := !native && ft.Kind() == reflect.Struct\n\t\tfor _, fv := range arr {\n\t\t\tvar sv reflect.Value\n\t\t\tif recursive {\n\t\t\t\tif ptr {\n\t\t\t\t\tfv, err := l.qs.NameOf(fv)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tvar ok bool\n\t\t\t\t\tsv, ok = l.seen[fv]\n\t\t\t\t\tif ok && sv.Type().AssignableTo(f.Type) {\n\t\t\t\t\t\tdf.Set(sv)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsv = reflect.New(ft).Elem()\n\t\t\t\terr := l.loadIteratorToDepth(ctx, sv, depth-1, iterator.NewFixed(fv))\n\t\t\t\tif err == errRequiredFieldIsMissing {\n\t\t\t\t\tcontinue\n\t\t\t\t} else if err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfv, err := l.qs.NameOf(fv)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif fv == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsv = reflect.ValueOf(fv)\n\t\t\t}\n\t\t\tif err := DefaultConverter.SetValue(df, sv); err != nil {\n\t\t\t\treturn fmt.Errorf(\"field %s: %v\", f.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (l *loader) iteratorForType(ctx context.Context, root iterator.Shape, rt reflect.Type, rootOnly bool) (iterator.Shape, error) {\n\tp, err := l.makePathForType(rt, \"\", rootOnly)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn l.iteratorFromPath(ctx, root, p)\n}\n\nfunc mergeMap(dst map[string][]graph.Ref, m map[string]graph.Ref) {\nloop:\n\tfor k, v := range m {\n\t\tsl := dst[k]\n\t\tfor _, sv := range sl {\n\t\t\tif keysEqual(sv, v) {\n\t\t\t\tcontinue loop\n\t\t\t}\n\t\t}\n\t\tdst[k] = append(sl, v)\n\t}\n}\n\nfunc (l *loader) loadIteratorToDepth(ctx context.Context, dst reflect.Value, depth int, list iterator.Shape) error {\n\tif ctx == nil {\n\t\tctx = context.TODO()\n\t}\n\tif dst.Kind() == reflect.Ptr {\n\t\tdst = dst.Elem()\n\t}\n\tet := dst.Type()\n\tslice, chanl := false, false\n\tif dst.Kind() == reflect.Slice {\n\t\tet = et.Elem()\n\t\tslice = true\n\t} else if dst.Kind() == reflect.Chan {\n\t\tet = et.Elem()\n\t\tchanl = true\n\t\tdefer dst.Close()\n\t}\n\tfields, err := l.c.rulesFor(et)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctxDone := func() bool {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn true\n\t\tdefault:\n\t\t}\n\t\treturn false\n\t}\n\n\tif ctxDone() {\n\t\treturn ctx.Err()\n\t}\n\n\trootOnly := depth == 0\n\tits, err := l.iteratorForType(ctx, list, et, rootOnly)\n\tif err != nil {\n\t\treturn err\n\t}\n\tit := its.Iterate()\n\tdefer it.Close()\n\n\tctx = context.WithValue(ctx, fieldsCtxKey{}, fields)\n\tfor it.Next(ctx) {\n\t\tif ctxDone() {\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tid, err := l.qs.NameOf(it.Result())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif id != nil {\n\t\t\tif sv, ok := l.seen[id]; ok {\n\t\t\t\tif slice {\n\t\t\t\t\tdst.Set(reflect.Append(dst, sv.Elem()))\n\t\t\t\t} else if chanl {\n\t\t\t\t\tdst.Send(sv.Elem())\n\t\t\t\t} else if dst.Kind() != reflect.Ptr {\n\t\t\t\t\tdst.Set(sv.Elem())\n\t\t\t\t\treturn nil\n\t\t\t\t} else {\n\t\t\t\t\tdst.Set(sv)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tmp := make(map[string]graph.Ref)\n\t\tit.TagResults(mp)\n\t\tif len(mp) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tcur := dst\n\t\tif slice || chanl {\n\t\t\tcur = reflect.New(et)\n\t\t}\n\t\tmo := make(map[string][]graph.Ref, len(mp))\n\t\tfor k, v := range mp {\n\t\t\tmo[k] = []graph.Ref{v}\n\t\t}\n\t\tfor it.NextPath(ctx) {\n\t\t\tif ctxDone() {\n\t\t\t\treturn ctx.Err()\n\t\t\t}\n\t\t\tmp = make(map[string]graph.Ref)\n\t\t\tit.TagResults(mp)\n\t\t\tif len(mp) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// TODO(dennwc): replace with something more efficient\n\t\t\tmergeMap(mo, mp)\n\t\t}\n\t\tif id != nil {\n\t\t\tsv := cur\n\t\t\tif sv.Kind() != reflect.Ptr && sv.CanAddr() {\n\t\t\t\tsv = sv.Addr()\n\t\t\t}\n\t\t\tl.seen[id] = sv\n\t\t}\n\t\terr = l.loadToValue(ctx, cur, depth, mo, \"\")\n\t\tif err == errRequiredFieldIsMissing {\n\t\t\tif !slice && !chanl {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif slice {\n\t\t\tdst.Set(reflect.Append(dst, cur.Elem()))\n\t\t} else if chanl {\n\t\t\tdst.Send(cur.Elem())\n\t\t} else {\n\t\t\treturn nil\n\t\t}\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn err\n\t}\n\tif slice || chanl {\n\t\treturn nil\n\t}\n\tif list != nil { // TODO(dennwc): optional optimization: do this only if iterator is not \"all nodes\"\n\t\t// distinguish between missing object and type constraints\n\t\tand := iterator.NewAnd(list, l.qs.NodesAllIterator()).Iterate()\n\t\tdefer and.Close()\n\t\tif and.Next(ctx) {\n\t\t\treturn errRequiredFieldIsMissing\n\t\t}\n\t}\n\treturn errNotFound\n}\n\nfunc (l *loader) iteratorFromPath(ctx context.Context, root iterator.Shape, p *path.Path) (iterator.Shape, error) {\n\tit := p.BuildIteratorOn(ctx, l.qs)\n\tif root != nil {\n\t\tit = iterator.NewAnd(root, it)\n\t}\n\tif Optimize {\n\t\tit, _ = it.Optimize(ctx)\n\t}\n\treturn it, nil\n}\n"
  },
  {
    "path": "schema/loader_test.go",
    "content": "package schema_test\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph/iterator\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestLoadLoop(t *testing.T) {\n\tsch := schema.NewConfig()\n\n\ta := &NodeLoop{ID: iri(\"A\"), Name: \"Node A\"}\n\ta.Next = a\n\n\tqs := memstore.New([]quad.Quad{\n\t\t{a.ID, iri(\"name\"), quad.String(a.Name), nil},\n\t\t{a.ID, iri(\"next\"), a.ID, nil},\n\t}...)\n\n\tb := &NodeLoop{}\n\tif err := sch.LoadIteratorTo(nil, qs, reflect.ValueOf(b), nil); err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tif a.ID != b.ID || a.Name != b.Name {\n\t\tt.Fatalf(\"%#v vs %#v\", a, b)\n\t}\n\tif b != b.Next {\n\t\tt.Fatalf(\"loop is broken: %p vs %p\", b, b.Next)\n\t}\n\n\ta = &NodeLoop{ID: iri(\"A\"), Name: \"Node A\"}\n\tb = &NodeLoop{ID: iri(\"B\"), Name: \"Node B\"}\n\tc := &NodeLoop{ID: iri(\"C\"), Name: \"Node C\"}\n\ta.Next = b\n\tb.Next = c\n\tc.Next = a\n\n\tqs = memstore.New([]quad.Quad{\n\t\t{a.ID, iri(\"name\"), quad.String(a.Name), nil},\n\t\t{b.ID, iri(\"name\"), quad.String(b.Name), nil},\n\t\t{c.ID, iri(\"name\"), quad.String(c.Name), nil},\n\t\t{a.ID, iri(\"next\"), b.ID, nil},\n\t\t{b.ID, iri(\"next\"), c.ID, nil},\n\t\t{c.ID, iri(\"next\"), a.ID, nil},\n\t}...)\n\n\ta1 := &NodeLoop{}\n\tif err := sch.LoadIteratorTo(nil, qs, reflect.ValueOf(a1), nil); err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tif a.ID != a1.ID || a.Name != a1.Name {\n\t\tt.Fatalf(\"%#v vs %#v\", a, b)\n\t}\n\tb1 := a1.Next\n\tc1 := b1.Next\n\tif b.ID != b1.ID || b.Name != b1.Name {\n\t\tt.Fatalf(\"%#v vs %#v\", a, b)\n\t}\n\tif c.ID != c1.ID || c.Name != c1.Name {\n\t\tt.Fatalf(\"%#v vs %#v\", a, b)\n\t}\n\tif a1 != c1.Next {\n\t\tt.Fatalf(\"loop is broken: %p vs %p\", a1, c1.Next)\n\t}\n}\n\nfunc TestLoadIteratorTo(t *testing.T) {\n\tsch := schema.NewConfig()\n\tfor i, c := range testFillValueCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tqs := memstore.New(c.quads...)\n\t\t\trt := reflect.TypeOf(c.expect)\n\t\t\tvar out reflect.Value\n\t\t\tif rt.Kind() == reflect.Ptr {\n\t\t\t\tout = reflect.New(rt.Elem())\n\t\t\t} else {\n\t\t\t\tout = reflect.New(rt)\n\t\t\t}\n\t\t\tvar it iterator.Shape\n\t\t\tif c.from != nil {\n\t\t\t\tfixed := iterator.NewFixed()\n\t\t\t\tfor _, id := range c.from {\n\t\t\t\t\tqsv, err := qs.ValueOf(id)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tfixed.Add(qsv)\n\t\t\t\t}\n\t\t\t\tit = fixed\n\t\t\t}\n\t\t\tdepth := c.depth\n\t\t\tif depth == 0 {\n\t\t\t\tdepth = -1\n\t\t\t}\n\t\t\tif err := sch.LoadIteratorToDepth(nil, qs, out, depth, it); err != nil {\n\t\t\t\tt.Errorf(\"case %d failed: %v\", i+1, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar got interface{}\n\t\t\tif rt.Kind() == reflect.Ptr {\n\t\t\t\tgot = out.Interface()\n\t\t\t} else {\n\t\t\t\tgot = out.Elem().Interface()\n\t\t\t}\n\t\t\tif s, ok := got.(interface {\n\t\t\t\tSort()\n\t\t\t}); ok {\n\t\t\t\ts.Sort()\n\t\t\t}\n\t\t\tif s, ok := c.expect.(interface {\n\t\t\t\tSort()\n\t\t\t}); ok {\n\t\t\t\ts.Sort()\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, c.expect) {\n\t\t\t\tt.Errorf(\"case %d failed: objects are different\\n%#v\\n%#v\",\n\t\t\t\t\ti+1, got, c.expect,\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar testFillValueCases = []struct {\n\tname   string\n\texpect interface{}\n\tquads  []quad.Quad\n\tdepth  int\n\tfrom   []quad.Value\n}{\n\t{\n\t\tname: \"complex object\",\n\t\texpect: struct {\n\t\t\trdfType struct{} `quad:\"rdf:type > some:Type\"`\n\t\t\tID      quad.IRI `quad:\"@id\"`\n\t\t\tName    string   `quad:\"name\"`\n\t\t\tValues  []string `quad:\"values\"`\n\t\t\tItems   []item   `quad:\"items\"`\n\t\t\tSub     *item    `quad:\"sub\"`\n\t\t\tVal     int      `quad:\"val\"`\n\t\t}{\n\t\t\tID:     \"1234\",\n\t\t\tName:   \"some item\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t\tItems: []item{\n\t\t\t\t{ID: \"sub1\", Name: \"Sub 1\"},\n\t\t\t\t{ID: \"sub2\", Name: \"Sub 2\"},\n\t\t\t},\n\t\t\tSub: &item{ID: \"sub3\", Name: \"Sub 3\"},\n\t\t\tVal: 123,\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"some item\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t\t{iri(\"sub1\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub1\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"items\"), iri(\"sub1\"), nil},\n\t\t\t{iri(\"sub2\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub2\"), iri(\"name\"), quad.String(\"Sub 2\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"items\"), iri(\"sub2\"), nil},\n\t\t\t{iri(\"sub3\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub3\"), iri(\"name\"), quad.String(\"Sub 3\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"sub\"), iri(\"sub3\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"val\"), quad.Int(123), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"complex object (id value)\",\n\t\texpect: struct {\n\t\t\trdfType struct{}   `quad:\"rdf:type > some:Type\"`\n\t\t\tID      quad.Value `quad:\"@id\"`\n\t\t\tName    string     `quad:\"name\"`\n\t\t\tValues  []string   `quad:\"values\"`\n\t\t\tItems   []item     `quad:\"items\"`\n\t\t}{\n\t\t\tID:     quad.BNode(\"1234\"),\n\t\t\tName:   \"some item\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t\tItems: []item{\n\t\t\t\t{ID: \"sub1\", Name: \"Sub 1\"},\n\t\t\t\t{ID: \"sub2\", Name: \"Sub 2\"},\n\t\t\t},\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{quad.BNode(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{quad.BNode(\"1234\"), iri(\"name\"), quad.String(\"some item\"), nil},\n\t\t\t{quad.BNode(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{quad.BNode(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t\t{iri(\"sub1\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub1\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{quad.BNode(\"1234\"), iri(\"items\"), iri(\"sub1\"), nil},\n\t\t\t{iri(\"sub2\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub2\"), iri(\"name\"), quad.String(\"Sub 2\"), nil},\n\t\t\t{quad.BNode(\"1234\"), iri(\"items\"), iri(\"sub2\"), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"embedded object\",\n\t\texpect: struct {\n\t\t\trdfType struct{} `quad:\"rdf:type > some:Type\"`\n\t\t\titem2\n\t\t\tID     quad.IRI `quad:\"@id\"`\n\t\t\tValues []string `quad:\"values\"`\n\t\t}{\n\t\t\titem2:  item2{Name: \"Sub 1\", Spec: \"special\"},\n\t\t\tID:     \"1234\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"spec\"), quad.String(\"special\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"type shorthand\",\n\t\texpect: struct {\n\t\t\trdfType struct{} `quad:\"@type > some:Type\"`\n\t\t\titem2\n\t\t\tID     quad.IRI `quad:\"@id\"`\n\t\t\tValues []string `quad:\"values\"`\n\t\t}{\n\t\t\titem2:  item2{Name: \"Sub 1\", Spec: \"special\"},\n\t\t\tID:     \"1234\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"spec\"), quad.String(\"special\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"tree\",\n\t\texpect: treeItem{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t\tChildren: []treeItem{\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n2\"),\n\t\t\t\t\tName: \"Node 2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n3\"),\n\t\t\t\t\tName: \"Node 3\",\n\t\t\t\t\tChildren: []treeItem{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tID:   iri(\"n4\"),\n\t\t\t\t\t\t\tName: \"Node 4\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tquads: treeQuads,\n\t\tfrom:  []quad.Value{iri(\"n1\")},\n\t},\n\t{\n\t\tname: \"tree with depth limit 1\",\n\t\texpect: treeItem{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t\tChildren: []treeItem{\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n2\"),\n\t\t\t\t\tName: \"Node 2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n3\"),\n\t\t\t\t\tName: \"Node 3\",\n\t\t\t\t\tChildren: []treeItem{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tID: iri(\"n4\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tdepth: 1,\n\t\tquads: treeQuads,\n\t\tfrom:  []quad.Value{iri(\"n1\")},\n\t},\n\t{\n\t\tname: \"tree with depth limit 2\",\n\t\texpect: treeItemOpt{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t\tChildren: []treeItemOpt{\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n2\"),\n\t\t\t\t\tName: \"Node 2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n3\"),\n\t\t\t\t\tName: \"Node 3\",\n\t\t\t\t\tChildren: []treeItemOpt{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tID:   iri(\"n4\"),\n\t\t\t\t\t\t\tName: \"Node 4\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tdepth: 2,\n\t\tquads: treeQuads,\n\t\tfrom:  []quad.Value{iri(\"n1\")},\n\t},\n\t{\n\t\tname: \"tree with required children\",\n\t\texpect: treeItemReq{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t\tChildren: []treeItemReq{\n\t\t\t\t{\n\t\t\t\t\tID:   iri(\"n3\"),\n\t\t\t\t\tName: \"Node 3\",\n\t\t\t\t\t// TODO(dennwc): a strange behavior: this field is required, but it's empty for current object,\n\t\t\t\t\t// because all it's children are missing the same field. Leaving this as-is for now because\n\t\t\t\t\t// it's weird to set Children field as required in a tree.\n\t\t\t\t\tChildren: nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tquads: treeQuads,\n\t\tfrom:  []quad.Value{iri(\"n1\")},\n\t},\n\t{\n\t\tname: \"simple object\",\n\t\texpect: subObject{\n\t\t\tgenObject: genObject{\n\t\t\t\tID:   \"1234\",\n\t\t\t\tName: \"Obj\",\n\t\t\t},\n\t\t\tNum: 3,\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Obj\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"num\"), quad.Int(3), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"typedef\",\n\t\texpect: genObjectTypedef{\n\t\t\tID:   \"1234\",\n\t\t\tName: \"Obj\",\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Obj\"), nil},\n\t\t},\n\t},\n\t{\n\t\tname:   \"coords\",\n\t\texpect: Coords{Lat: 12.3, Lng: 34.5},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"c1\"), typeIRI, iri(\"ex:Coords\"), nil},\n\t\t\t{iri(\"c1\"), iri(\"ex:lat\"), quad.Float(12.3), nil},\n\t\t\t{iri(\"c1\"), iri(\"ex:lng\"), quad.Float(34.5), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"same node\",\n\t\texpect: NestedNode{\n\t\t\tID:   \"c1\",\n\t\t\tName: \"A\",\n\t\t\tPrev: genObject{\n\t\t\t\tID:   \"c2\",\n\t\t\t\tName: \"B\",\n\t\t\t},\n\t\t\tNext: genObject{\n\t\t\t\tID:   \"c2\",\n\t\t\t\tName: \"B\",\n\t\t\t},\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"c1\"), iri(\"name\"), quad.String(\"A\"), nil},\n\t\t\t{iri(\"c2\"), iri(\"name\"), quad.String(\"B\"), nil},\n\t\t\t{iri(\"c1\"), iri(\"next\"), iri(\"c2\"), nil},\n\t\t\t{iri(\"c1\"), iri(\"prev\"), iri(\"c2\"), nil},\n\t\t},\n\t},\n\t{\n\t\tname: \"all optional\",\n\t\texpect: Alts{\n\t\t\tAlt: []OptFields{\n\t\t\t\t{One: \"A\"},\n\t\t\t\t{Two: \"B\"},\n\t\t\t\t{One: \"C\", Two: \"D\"},\n\t\t\t},\n\t\t},\n\t\tquads: []quad.Quad{\n\t\t\t{iri(\"c1\"), iri(\"alt\"), iri(\"h1\"), nil},\n\t\t\t{iri(\"c1\"), iri(\"alt\"), iri(\"h2\"), nil},\n\t\t\t{iri(\"c1\"), iri(\"alt\"), iri(\"h3\"), nil},\n\n\t\t\t{iri(\"h1\"), iri(\"one\"), quad.String(\"A\"), nil},\n\t\t\t{iri(\"h2\"), iri(\"two\"), quad.String(\"B\"), nil},\n\t\t\t{iri(\"h3\"), iri(\"one\"), quad.String(\"C\"), nil},\n\t\t\t{iri(\"h3\"), iri(\"two\"), quad.String(\"D\"), nil},\n\t\t},\n\t},\n}\n"
  },
  {
    "path": "schema/namespaces.go",
    "content": "package schema\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\ntype namespace struct {\n\t_      struct{} `quad:\"@type > cayley:namespace\"`\n\tFull   quad.IRI `quad:\"@id\"`\n\tPrefix quad.IRI `quad:\"cayley:prefix\"`\n}\n\n// WriteNamespaces will writes namespaces list into graph.\nfunc (c *Config) WriteNamespaces(w quad.Writer, n *voc.Namespaces) error {\n\trules, err := c.rulesFor(reflect.TypeOf(namespace{}))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't load rules: %v\", err)\n\t}\n\twr := c.newWriter(w)\n\tfor _, ns := range n.List() {\n\t\tobj := namespace{\n\t\t\tFull:   quad.IRI(ns.Full),\n\t\t\tPrefix: quad.IRI(ns.Prefix),\n\t\t}\n\t\trv := reflect.ValueOf(obj)\n\t\tif err = wr.writeValueAs(obj.Full, rv, \"\", rules); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// LoadNamespaces will load namespaces stored in graph to a specified list.\n// If destination list is empty, global namespace registry will be used.\nfunc (c *Config) LoadNamespaces(ctx context.Context, qs graph.QuadStore, dest *voc.Namespaces) error {\n\tvar list []namespace\n\tif err := c.LoadTo(ctx, qs, &list); err != nil {\n\t\treturn err\n\t}\n\tregister := dest.Register\n\tif dest == nil {\n\t\tregister = voc.Register\n\t}\n\tfor _, ns := range list {\n\t\tregister(voc.Namespace{\n\t\t\tPrefix: string(ns.Prefix),\n\t\t\tFull:   string(ns.Full),\n\t\t})\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "schema/namespaces_test.go",
    "content": "package schema_test\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nfunc TestSaveNamespaces(t *testing.T) {\n\tsch := schema.NewConfig()\n\tsave := []voc.Namespace{\n\t\t{Full: \"http://example.org/\", Prefix: \"ex:\"},\n\t\t{Full: \"http://cayley.io/\", Prefix: \"c:\"},\n\t}\n\tvar ns voc.Namespaces\n\tfor _, n := range save {\n\t\tns.Register(n)\n\t}\n\tqs := memstore.New()\n\terr := sch.WriteNamespaces(qs, &ns)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar ns2 voc.Namespaces\n\terr = sch.LoadNamespaces(context.TODO(), qs, &ns2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tgot := ns2.List()\n\tsort.Sort(voc.ByFullName(save))\n\tsort.Sort(voc.ByFullName(got))\n\tif !reflect.DeepEqual(save, got) {\n\t\tt.Fatalf(\"wrong namespaces returned: got: %v, expect: %v\", got, save)\n\t}\n\tqr := graph.NewQuadStoreReader(qs)\n\tq, err := quad.ReadAll(qr)\n\tqr.Close()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\texpect := []quad.Quad{\n\t\tquad.MakeIRI(\"http://cayley.io/\", \"cayley:prefix\", \"c:\", \"\"),\n\t\tquad.MakeIRI(\"http://cayley.io/\", \"rdf:type\", \"cayley:namespace\", \"\"),\n\n\t\tquad.MakeIRI(\"http://example.org/\", \"cayley:prefix\", \"ex:\", \"\"),\n\t\tquad.MakeIRI(\"http://example.org/\", \"rdf:type\", \"cayley:namespace\", \"\"),\n\t}\n\tsort.Sort(quad.ByQuadString(expect))\n\tsort.Sort(quad.ByQuadString(q))\n\tif !reflect.DeepEqual(expect, q) {\n\t\tt.Fatalf(\"wrong quads returned: got: %v, expect: %v\", q, expect)\n\t}\n}\n"
  },
  {
    "path": "schema/schema.go",
    "content": "// Package schema contains helpers to map Go objects to quads and vise-versa.\n//\n// This package is not a full schema library. It will not save or force any\n// RDF schema constrains, it only provides a mapping.\npackage schema\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query/path\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n)\n\nvar global = NewConfig()\n\n// Global returns a default global schema config.\nfunc Global() *Config {\n\treturn global\n}\n\nvar reflQuadValue = reflect.TypeOf((*quad.Value)(nil)).Elem()\n\ntype ErrReqFieldNotSet struct {\n\tField string\n}\n\nfunc (e ErrReqFieldNotSet) Error() string {\n\treturn fmt.Sprintf(\"required field is not set: %s\", e.Field)\n}\n\n// IRIMode controls how IRIs are processed.\ntype IRIMode int\n\nconst (\n\t// IRINative applies no transformation to IRIs.\n\tIRINative = IRIMode(iota)\n\t// IRIShort will compact all IRIs with known namespaces.\n\tIRIShort\n\t// IRIFull will expand all IRIs with known namespaces.\n\tIRIFull\n)\n\n// NewConfig creates a new schema config.\nfunc NewConfig() *Config {\n\treturn &Config{\n\t\tIRIs: IRINative,\n\t}\n}\n\n// Config controls behavior of schema package.\ntype Config struct {\n\t// IRIs set a conversion mode for all IRIs.\n\tIRIs IRIMode\n\n\t// GenerateID is called when any object without an ID field is being saved.\n\tGenerateID func(_ interface{}) quad.Value\n\n\t// Label will be added to all quads written. Does not affect queries.\n\tLabel quad.Value\n\n\trulesForTypeMu sync.RWMutex\n\trulesForType   map[reflect.Type]fieldRules\n}\n\nfunc (c *Config) genID(o interface{}) quad.Value {\n\tgen := c.GenerateID\n\tif gen == nil {\n\t\tgen = func(_ interface{}) quad.Value {\n\t\t\treturn quad.RandomBlankNode()\n\t\t}\n\t}\n\treturn gen(o)\n}\n\ntype rule interface {\n\tisRule()\n}\n\ntype constraintRule struct {\n\tPred quad.IRI\n\tVal  quad.IRI\n\tRev  bool\n}\n\nfunc (constraintRule) isRule() {}\n\ntype saveRule struct {\n\tPred quad.IRI\n\tRev  bool\n\tOpt  bool\n}\n\nfunc (saveRule) isRule() {}\n\ntype idRule struct{}\n\nfunc (idRule) isRule() {}\n\nconst iriType = quad.IRI(rdf.Type)\n\nfunc (c *Config) iri(v quad.IRI) quad.IRI {\n\tswitch c.IRIs {\n\tcase IRIShort:\n\t\tv = v.Short()\n\tcase IRIFull:\n\t\tv = v.Full()\n\t}\n\treturn v\n}\n\nfunc (c *Config) toIRI(s string) quad.IRI {\n\tvar v quad.IRI\n\tif s == \"@type\" {\n\t\tv = iriType\n\t} else {\n\t\tv = quad.IRI(s)\n\t}\n\treturn c.iri(v)\n}\n\nvar reflEmptyStruct = reflect.TypeOf(struct{}{})\n\nfunc (c *Config) fieldRule(fld reflect.StructField) (rule, error) {\n\ttag := fld.Tag.Get(\"quad\")\n\tsub := strings.Split(tag, \",\")\n\ttag, sub = sub[0], sub[1:]\n\tconst (\n\t\ttrim      = ` `\n\t\tspo, ops  = `>`, `<`\n\t\tany, none = `*`, `-`\n\t\tthis      = `@id`\n\t)\n\ttag = strings.Trim(tag, trim)\n\tjsn := false\n\tif tag == \"\" {\n\t\ttag = strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\tjsn = true\n\t}\n\tif tag == \"\" || tag == none {\n\t\treturn nil, nil // ignore\n\t}\n\trule := strings.Trim(tag, trim)\n\tif rule == this {\n\t\treturn idRule{}, nil\n\t}\n\topt := false\n\treq := false\n\tfor _, s := range sub {\n\t\tif s == \"opt\" || s == \"optional\" {\n\t\t\topt = true\n\t\t}\n\t\tif s == \"req\" || s == \"required\" {\n\t\t\treq = true\n\t\t}\n\t}\n\tif req {\n\t\topt = false\n\t} else if fld.Type.Kind() == reflect.Slice {\n\t\topt = true\n\t}\n\n\trev := strings.Contains(rule, ops)\n\tvar tri []string\n\tif jsn {\n\t\ttri = []string{rule}\n\t} else if rev { // o<p-s\n\t\ttri = strings.SplitN(rule, ops, 3)\n\t\tif len(tri) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"wrong quad tag format: '%s'\", rule)\n\t\t}\n\t} else { // s-p>o // default\n\t\ttri = strings.SplitN(rule, spo, 3)\n\t\tif len(tri) > 2 { //len(tri) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"wrong quad tag format: '%s'\", rule)\n\t\t}\n\t}\n\tvar ps, vs string\n\tif rev {\n\t\tps, vs = strings.Trim(tri[0], trim), strings.Trim(tri[1], trim)\n\t} else {\n\t\tps, vs = strings.Trim(tri[0], trim), any\n\t\tif len(tri) > 1 {\n\t\t\tvs = strings.Trim(tri[1], trim)\n\t\t}\n\t}\n\tif ps == \"\" {\n\t\treturn nil, fmt.Errorf(\"wrong quad format: '%s': no predicate\", rule)\n\t}\n\tp := c.toIRI(ps)\n\tif vs == \"\" || vs == any && fld.Type != reflEmptyStruct {\n\t\treturn saveRule{Pred: p, Rev: rev, Opt: opt}, nil\n\t}\n\treturn constraintRule{Pred: p, Val: c.toIRI(vs), Rev: rev}, nil\n}\n\nfunc checkFieldType(ftp reflect.Type) error {\n\tfor ftp.Kind() == reflect.Ptr || ftp.Kind() == reflect.Slice {\n\t\tftp = ftp.Elem()\n\t}\n\tswitch ftp.Kind() {\n\tcase reflect.Array: // TODO: support arrays\n\t\treturn fmt.Errorf(\"array fields are not supported yet\")\n\tcase reflect.Func, reflect.Invalid:\n\t\treturn fmt.Errorf(\"%v fields are not supported\", ftp.Kind())\n\tdefault:\n\t}\n\treturn nil\n}\n\nvar (\n\ttypesMu   sync.RWMutex\n\ttypeToIRI = make(map[reflect.Type]quad.IRI)\n\tiriToType = make(map[quad.IRI]reflect.Type)\n)\n\nfunc getTypeIRI(rt reflect.Type) quad.IRI {\n\ttypesMu.RLock()\n\tiri := typeToIRI[rt]\n\ttypesMu.RUnlock()\n\treturn iri\n}\n\n// RegisterType associates an IRI with a given Go type.\n//\n// All queries and writes will require or add a type triple.\nfunc RegisterType(iri quad.IRI, obj interface{}) {\n\tvar rt reflect.Type\n\tif obj != nil {\n\t\tif t, ok := obj.(reflect.Type); ok {\n\t\t\trt = t\n\t\t} else {\n\t\t\trt = reflect.TypeOf(obj)\n\t\t\tif rt.Kind() == reflect.Ptr {\n\t\t\t\trt = rt.Elem()\n\t\t\t}\n\t\t}\n\t}\n\tfull := iri.Full()\n\ttypesMu.Lock()\n\tdefer typesMu.Unlock()\n\tif obj == nil {\n\t\ttp := iriToType[full]\n\t\tdelete(typeToIRI, tp)\n\t\tdelete(iriToType, full)\n\t\treturn\n\t}\n\tif _, exists := typeToIRI[rt]; exists {\n\t\tpanic(fmt.Errorf(\"type %v is already registered\", rt))\n\t}\n\tif _, exists := iriToType[full]; exists {\n\t\tpanic(fmt.Errorf(\"IRI %v is already registered\", iri))\n\t}\n\ttypeToIRI[rt] = iri\n\tiriToType[full] = rt\n}\n\n// PathForType builds a path (morphism) for a given Go type.\nfunc (c *Config) PathForType(rt reflect.Type) (*path.Path, error) {\n\tl := c.newLoader(nil)\n\treturn l.makePathForType(rt, \"\", false)\n}\n\nfunc anonFieldType(fld reflect.StructField) (reflect.Type, bool) {\n\tft := fld.Type\n\tif ft.Kind() == reflect.Ptr {\n\t\tft = ft.Elem()\n\t}\n\tif ft.Kind() == reflect.Struct {\n\t\treturn ft, true\n\t}\n\treturn ft, false\n}\n\nfunc (c *Config) rulesForStructTo(out fieldRules, pref string, rt reflect.Type) error {\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tf := rt.Field(i)\n\t\tname := f.Name\n\t\tif f.Anonymous {\n\t\t\tif ft, ok := anonFieldType(f); !ok {\n\t\t\t\treturn fmt.Errorf(\"anonymous fields of type %v are not supported\", ft)\n\t\t\t} else if err := c.rulesForStructTo(out, pref+name+\".\", ft); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\trules, err := c.fieldRule(f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif rules != nil {\n\t\t\tout[pref+name] = rules\n\t\t}\n\t}\n\treturn nil\n}\n\n// rulesFor\n//\n// Returned map should not be changed.\nfunc (c *Config) rulesFor(rt reflect.Type) (fieldRules, error) {\n\t//\tif rt.Kind() != reflect.Struct {\n\t//\t\treturn nil, fmt.Errorf(\"expected struct, got: %v\", rt)\n\t//\t}\n\tc.rulesForTypeMu.RLock()\n\trules, ok := c.rulesForType[rt]\n\tc.rulesForTypeMu.RUnlock()\n\tif ok {\n\t\treturn rules, nil\n\t}\n\tout := make(fieldRules)\n\tif err := c.rulesForStructTo(out, \"\", rt); err != nil {\n\t\treturn nil, err\n\t}\n\tc.rulesForTypeMu.Lock()\n\tif c.rulesForType == nil {\n\t\tc.rulesForType = make(map[reflect.Type]fieldRules)\n\t}\n\tc.rulesForType[rt] = out\n\tc.rulesForTypeMu.Unlock()\n\treturn out, nil\n}\n\ntype fieldsCtxKey struct{}\ntype fieldRules map[string]rule\n\ntype ValueConverter interface {\n\tSetValue(dst reflect.Value, src reflect.Value) error\n}\n\ntype ValueConverterFunc func(dst reflect.Value, src reflect.Value) error\n\nfunc (f ValueConverterFunc) SetValue(dst reflect.Value, src reflect.Value) error { return f(dst, src) }\n\nvar DefaultConverter ValueConverter\n\ntype ErrTypeConversionFailed struct {\n\tFrom reflect.Type\n\tTo   reflect.Type\n}\n\nfunc (e ErrTypeConversionFailed) Error() string {\n\treturn fmt.Sprintf(\"cannot convert %v to %v\", e.From, e.To)\n}\n\nfunc init() {\n\tDefaultConverter = ValueConverterFunc(func(dst reflect.Value, src reflect.Value) error {\n\t\tdt, st := dst.Type(), src.Type()\n\t\tif dt == st || (dt.Kind() == reflect.Interface && st.Implements(dt)) {\n\t\t\tdst.Set(src)\n\t\t\treturn nil\n\t\t} else if st.ConvertibleTo(dt) {\n\t\t\tdst.Set(src.Convert(dt))\n\t\t\treturn nil\n\t\t} else if dt.Kind() == reflect.Ptr {\n\t\t\tv := reflect.New(dt.Elem())\n\t\t\tif err := DefaultConverter.SetValue(v.Elem(), src); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdst.Set(v)\n\t\t\treturn nil\n\t\t} else if dt.Kind() == reflect.Slice {\n\t\t\tv := reflect.New(dt.Elem())\n\t\t\tif err := DefaultConverter.SetValue(v.Elem(), src); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdst.Set(reflect.Append(dst, v.Elem()))\n\t\t\treturn nil\n\t\t}\n\t\treturn ErrTypeConversionFailed{From: src.Type(), To: dst.Type()}\n\t})\n}\n\nfunc isNative(rt reflect.Type) bool { // TODO(dennwc): replace\n\t_, ok := quad.AsValue(reflect.Zero(rt).Interface())\n\treturn ok\n}\n\nfunc keysEqual(v1, v2 graph.Ref) bool {\n\ttype key interface {\n\t\tKey() interface{}\n\t}\n\te1, ok1 := v1.(key)\n\te2, ok2 := v2.(key)\n\tif ok1 != ok2 {\n\t\treturn false\n\t}\n\tif ok1 && ok2 {\n\t\treturn e1.Key() == e2.Key()\n\t}\n\treturn v1 == v2\n}\n\nfunc isExported(name string) bool {\n\tch, _ := utf8.DecodeRuneInString(name)\n\treturn unicode.IsUpper(ch)\n}\n\nfunc isZero(rv reflect.Value) bool {\n\tswitch rv.Kind() {\n\tcase reflect.Ptr:\n\t\treturn rv.IsNil()\n\tcase reflect.Slice, reflect.Map:\n\t\treturn rv.IsNil() || rv.Len() == 0\n\tcase reflect.Struct:\n\t\t// have to be careful here - struct may contain slice fields,\n\t\t// so we cannot compare them directly\n\t\trt := rv.Type()\n\t\texported := 0\n\t\tfor i := 0; i < rt.NumField(); i++ {\n\t\t\tf := rt.Field(i)\n\t\t\tif !isExported(f.Name) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\texported++\n\t\t\tif !isZero(rv.Field(i)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tif exported != 0 {\n\t\t\treturn true\n\t\t}\n\t\t// opaque type - compare directly\n\t}\n\t// primitive types\n\treturn rv.Interface() == reflect.Zero(rv.Type()).Interface()\n}\n\nfunc (c *Config) idFor(rules fieldRules, rt reflect.Type, rv reflect.Value, pref string) (id quad.Value, err error) {\n\thasAnon := false\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tfld := rt.Field(i)\n\t\thasAnon = hasAnon || fld.Anonymous\n\t\tif _, ok := rules[pref+fld.Name].(idRule); ok {\n\t\t\tvid := rv.Field(i).Interface()\n\t\t\tswitch vid := vid.(type) {\n\t\t\tcase quad.IRI:\n\t\t\t\tid = c.iri(vid)\n\t\t\tcase quad.BNode:\n\t\t\t\tid = vid\n\t\t\tcase string:\n\t\t\t\tid = c.toIRI(vid)\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"unsupported type for id field: %T\", vid)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tif !hasAnon {\n\t\treturn\n\t}\n\t// second pass - look for anonymous fields\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tfld := rt.Field(i)\n\t\tif !fld.Anonymous {\n\t\t\tcontinue\n\t\t}\n\t\tid, err = c.idFor(rules, fld.Type, rv.Field(i), pref+fld.Name+\".\")\n\t\tif err != nil || id != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "schema/schema_test.go",
    "content": "package schema_test\n\nimport (\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n\t\"github.com/cayleygraph/quad/voc/rdf\"\n)\n\nfunc init() {\n\tvoc.RegisterPrefix(\"ex:\", \"http://example.org/\")\n\tschema.RegisterType(quad.IRI(\"ex:Coords\"), Coords{})\n}\n\ntype item struct {\n\trdfType struct{} `quad:\"rdf:type > some:item\"`\n\tID      quad.IRI `quad:\"@id\"`\n\tName    string   `quad:\"name\"`\n\tSpec    string   `quad:\"spec,optional\"`\n}\n\ntype item2 struct {\n\tName string `quad:\"name\"`\n\tSpec string `quad:\"spec\"`\n}\n\ntype timeItem struct {\n\tID   quad.IRI  `quad:\"@id\"`\n\tName string    `quad:\"name\"`\n\tTS   time.Time `quad:\"ts\"`\n}\n\ntype treeItemByIRI []treeItem\n\nfunc (o treeItemByIRI) Len() int           { return len(o) }\nfunc (o treeItemByIRI) Less(i, j int) bool { return o[i].ID < o[j].ID }\nfunc (o treeItemByIRI) Swap(i, j int)      { o[i], o[j] = o[j], o[i] }\n\ntype treeItem struct {\n\tID       quad.IRI   `quad:\"@id\"`\n\tName     string     `quad:\"name\"`\n\tChildren []treeItem `quad:\"child\"`\n}\n\nfunc (t *treeItem) Sort() {\n\tfor _, c := range t.Children {\n\t\tc.Sort()\n\t}\n\tsort.Sort(treeItemByIRI(t.Children))\n}\n\ntype treeItemOptByIRI []treeItemOpt\n\nfunc (o treeItemOptByIRI) Len() int           { return len(o) }\nfunc (o treeItemOptByIRI) Less(i, j int) bool { return o[i].ID < o[j].ID }\nfunc (o treeItemOptByIRI) Swap(i, j int)      { o[i], o[j] = o[j], o[i] }\n\ntype treeItemOpt struct {\n\tID       quad.IRI      `quad:\"@id\"`\n\tName     string        `quad:\"name\"`\n\tChildren []treeItemOpt `quad:\"child,optional\"`\n}\n\nfunc (t *treeItemOpt) Sort() {\n\tfor _, c := range t.Children {\n\t\tc.Sort()\n\t}\n\tsort.Sort(treeItemOptByIRI(t.Children))\n}\n\ntype treeItemReqByIRI []treeItemReq\n\nfunc (o treeItemReqByIRI) Len() int           { return len(o) }\nfunc (o treeItemReqByIRI) Less(i, j int) bool { return o[i].ID < o[j].ID }\nfunc (o treeItemReqByIRI) Swap(i, j int)      { o[i], o[j] = o[j], o[i] }\n\ntype treeItemReq struct {\n\tID       quad.IRI      `quad:\"@id\"`\n\tName     string        `quad:\"name\"`\n\tChildren []treeItemReq `quad:\"child,required\"`\n}\n\nfunc (t *treeItemReq) Sort() {\n\tfor _, c := range t.Children {\n\t\tc.Sort()\n\t}\n\tsort.Sort(treeItemReqByIRI(t.Children))\n}\n\ntype genObject struct {\n\tID   quad.IRI `quad:\"@id\"`\n\tName string   `quad:\"name\"`\n}\n\ntype MyString string\n\ntype genObjectTypedef struct {\n\tID   quad.IRI `quad:\"@id\"`\n\tName MyString `quad:\"name\"`\n}\n\ntype subObject struct {\n\tgenObject\n\tNum int `quad:\"num\"`\n}\n\ntype subSubObject struct {\n\tsubObject\n\tNum2 int `quad:\"num2\"`\n}\n\ntype Coords struct {\n\tLat float64 `json:\"ex:lat\"`\n\tLng float64 `json:\"ex:lng\"`\n}\n\ntype NodeLoop struct {\n\tID   quad.IRI  `quad:\"@id\"`\n\tName string    `quad:\"name\"`\n\tNext *NodeLoop `quad:\"next\"`\n}\n\ntype NestedNode struct {\n\tID   quad.IRI  `quad:\"@id\"`\n\tName string    `quad:\"name\"`\n\tPrev genObject `quad:\"prev,opt\"`\n\tNext genObject `quad:\"next,opt\"`\n}\n\ntype Alts struct {\n\tAlt []OptFields `quad:\"alt\"`\n}\n\ntype OptFields struct {\n\tOne string `quad:\"one,optional\"`\n\tTwo string `quad:\"two,optional\"`\n}\n\nfunc iri(s string) quad.IRI { return quad.IRI(s) }\n\nconst typeIRI = quad.IRI(rdf.Type)\n\nvar treeQuads = []quad.Quad{\n\t{iri(\"n1\"), iri(\"name\"), quad.String(\"Node 1\"), nil},\n\t{iri(\"n2\"), iri(\"name\"), quad.String(\"Node 2\"), nil},\n\t{iri(\"n3\"), iri(\"name\"), quad.String(\"Node 3\"), nil},\n\t{iri(\"n4\"), iri(\"name\"), quad.String(\"Node 4\"), nil},\n\t{iri(\"n5\"), iri(\"name\"), quad.String(\"Node 5\"), nil},\n\n\t{iri(\"n1\"), iri(\"child\"), iri(\"n2\"), nil},\n\t{iri(\"n1\"), iri(\"child\"), iri(\"n3\"), nil},\n\n\t{iri(\"n3\"), iri(\"child\"), iri(\"n4\"), nil},\n}\n"
  },
  {
    "path": "schema/types.go",
    "content": "package schema\n\nimport (\n\t\"github.com/cayleygraph/quad\"\n\t_ \"github.com/cayleygraph/quad/voc/rdf\"\n\t_ \"github.com/cayleygraph/quad/voc/rdfs\"\n\t\"github.com/cayleygraph/quad/voc/schema\"\n)\n\nfunc init() {\n\tRegisterType(quad.IRI(schema.Class), Class{})\n\tRegisterType(quad.IRI(schema.Property), Property{})\n}\n\n// Resource is a basic RDF resource object.\ntype Resource struct {\n\tID quad.IRI `quad:\"@id\"`\n\n\tLabel   string `quad:\"rdfs:label,optional\"`\n\tComment string `quad:\"rdfs:comment,optional\"`\n\n\tName        string `quad:\"schema:name,optional\"`\n\tDescription string `quad:\"schema:description,optional\"`\n}\n\ntype Property struct {\n\tResource\n\tInverseOf    quad.IRI   `quad:\"schema:inverseOf,optional\"`\n\tSupersededBy []quad.IRI `quad:\"schema:supersededBy,optional\"`\n\tExpects      []quad.IRI `quad:\"schema:rangeIncludes\"`\n}\n\ntype Class struct {\n\tResource\n\tProperties   []Property `quad:\"schema:domainIncludes < *,optional\"`\n\tSupersededBy []quad.IRI `quad:\"schema:supersededBy,optional\"`\n\tExtends      []quad.IRI `quad:\"rdfs:subClassOf,optional\"`\n}\n\ntype PropertiesByIRI []Property\n\nfunc (a PropertiesByIRI) Len() int      { return len(a) }\nfunc (a PropertiesByIRI) Swap(i, j int) { a[i], a[j] = a[j], a[i] }\nfunc (a PropertiesByIRI) Less(i, j int) bool {\n\treturn a[i].ID < a[j].ID\n}\n\ntype ClassesByIRI []Class\n\nfunc (a ClassesByIRI) Len() int      { return len(a) }\nfunc (a ClassesByIRI) Swap(i, j int) { a[i], a[j] = a[j], a[i] }\nfunc (a ClassesByIRI) Less(i, j int) bool {\n\treturn a[i].ID < a[j].ID\n}\n"
  },
  {
    "path": "schema/writer.go",
    "content": "package schema\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/cayleygraph/quad\"\n)\n\n// WriteAsQuads writes a single value in form of quads into specified quad writer.\n//\n// It returns an identifier of the object in the output sub-graph. If an object has\n// an annotated ID field, it's value will be converted to quad.Value and returned.\n// Otherwise, a new BNode will be generated using GenerateID function.\n//\n// See LoadTo for a list of quads mapping rules.\nfunc (c *Config) WriteAsQuads(w quad.Writer, o interface{}) (quad.Value, error) {\n\twr := c.newWriter(w)\n\treturn wr.writeAsQuads(reflect.ValueOf(o))\n}\n\ntype writer struct {\n\tc    *Config\n\tw    quad.Writer\n\tseen map[uintptr]quad.Value\n}\n\nfunc (c *Config) newWriter(w quad.Writer) *writer {\n\treturn &writer{c: c, w: w, seen: make(map[uintptr]quad.Value)}\n}\n\nfunc (w *writer) writeQuad(s, p, o quad.Value, rev bool) error {\n\tif rev {\n\t\ts, o = o, s\n\t}\n\treturn w.w.WriteQuad(quad.Quad{Subject: s, Predicate: p, Object: o, Label: w.c.Label})\n}\n\n// writeOneValReflect writes a set of quads corresponding to a value. It may omit writing quads if value is zero.\nfunc (w *writer) writeOneValReflect(id quad.Value, pred quad.Value, rv reflect.Value, rev bool) error {\n\tif isZero(rv) {\n\t\treturn nil\n\t}\n\t// write field value and get an ID\n\tsid, err := w.writeAsQuads(rv)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// write a quad pointing to this value\n\treturn w.writeQuad(id, pred, sid, rev)\n}\n\nfunc (w *writer) writeTypeInfo(id quad.Value, rt reflect.Type) error {\n\tiri := getTypeIRI(rt)\n\tif iri == quad.IRI(\"\") {\n\t\treturn nil\n\t}\n\treturn w.writeQuad(id, w.c.iri(iriType), w.c.iri(iri), false)\n}\n\nfunc (w *writer) writeValueAs(id quad.Value, rv reflect.Value, pref string, rules fieldRules) error {\n\tswitch kind := rv.Kind(); kind {\n\tcase reflect.Ptr, reflect.Map:\n\t\tptr := rv.Pointer()\n\t\tif _, ok := w.seen[ptr]; ok {\n\t\t\treturn nil\n\t\t}\n\t\tw.seen[ptr] = id\n\t\tif kind == reflect.Ptr {\n\t\t\trv = rv.Elem()\n\t\t}\n\t}\n\trt := rv.Type()\n\tif err := w.writeTypeInfo(id, rt); err != nil {\n\t\treturn err\n\t}\n\tfor i := 0; i < rt.NumField(); i++ {\n\t\tf := rt.Field(i)\n\t\tif f.Anonymous {\n\t\t\tif err := w.writeValueAs(id, rv.Field(i), pref+f.Name+\".\", rules); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch r := rules[pref+f.Name].(type) {\n\t\tcase constraintRule:\n\t\t\ts, o := id, quad.Value(r.Val)\n\t\t\tif r.Rev {\n\t\t\t\ts, o = o, s\n\t\t\t}\n\t\t\tif err := w.writeQuad(s, r.Pred, o, false); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase saveRule:\n\t\t\tif f.Type.Kind() == reflect.Slice {\n\t\t\t\tsl := rv.Field(i)\n\t\t\t\tfor j := 0; j < sl.Len(); j++ {\n\t\t\t\t\tif err := w.writeOneValReflect(id, r.Pred, sl.Index(j), r.Rev); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfv := rv.Field(i)\n\t\t\t\tif !r.Opt && isZero(fv) {\n\t\t\t\t\treturn ErrReqFieldNotSet{Field: f.Name}\n\t\t\t\t}\n\t\t\t\tif err := w.writeOneValReflect(id, r.Pred, fv, r.Rev); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (w *writer) writeAsQuads(rv reflect.Value) (quad.Value, error) {\n\trt := rv.Type()\n\t// if node is a primitive - return directly\n\tif rt.Implements(reflQuadValue) {\n\t\treturn rv.Interface().(quad.Value), nil\n\t}\n\tprv := rv\n\tkind := rt.Kind()\n\t// check if we've seen this node already\n\tswitch kind {\n\tcase reflect.Ptr, reflect.Map:\n\t\tptr := prv.Pointer()\n\t\tif sid, ok := w.seen[ptr]; ok {\n\t\t\treturn sid, nil\n\t\t}\n\t\tif kind == reflect.Ptr {\n\t\t\trv = rv.Elem()\n\t\t\trt = rv.Type()\n\t\t\tkind = rt.Kind()\n\t\t}\n\t}\n\t// check if it's a type that quads package supports\n\t// note, that it may be a struct such as time.Time\n\tif val, ok := quad.AsValue(rv.Interface()); ok {\n\t\treturn val, nil\n\t} else if kind == reflect.String {\n\t\treturn quad.String(rv.String()), nil\n\t} else if kind == reflect.Int || kind == reflect.Uint ||\n\t\tkind == reflect.Int32 || kind == reflect.Uint32 ||\n\t\tkind == reflect.Int16 || kind == reflect.Uint16 ||\n\t\tkind == reflect.Int8 || kind == reflect.Uint8 {\n\t\treturn quad.Int(rv.Int()), nil\n\t} else if kind == reflect.Float64 || kind == reflect.Float32 {\n\t\treturn quad.Float(rv.Float()), nil\n\t} else if kind == reflect.Bool {\n\t\treturn quad.Bool(rv.Bool()), nil\n\t}\n\t// TODO(dennwc): support maps\n\tif kind != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"unsupported type: %v\", rt)\n\t}\n\t// get conversion rules for this struct type\n\trules, err := w.c.rulesFor(rt)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load rules: %v\", err)\n\t}\n\tif len(rules) == 0 {\n\t\treturn nil, fmt.Errorf(\"no rules for struct: %v\", rt)\n\t}\n\t// get an ID from the struct value\n\tid, err := w.c.idFor(rules, rt, rv, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif id == nil {\n\t\tid = w.c.genID(prv.Interface())\n\t}\n\t// save a node ID to avoid loops\n\tswitch prv.Kind() {\n\tcase reflect.Ptr, reflect.Map:\n\t\tptr := prv.Pointer()\n\t\tw.seen[ptr] = id\n\t}\n\tif err = w.writeValueAs(id, rv, \"\", rules); err != nil {\n\t\treturn nil, err\n\t}\n\treturn id, nil\n}\n"
  },
  {
    "path": "schema/writer_test.go",
    "content": "package schema_test\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cayleygraph/cayley/schema\"\n\t\"github.com/cayleygraph/quad\"\n)\n\ntype quadSlice []quad.Quad\n\nfunc (s *quadSlice) WriteQuad(q quad.Quad) error {\n\t*s = append(*s, q)\n\treturn nil\n}\n\nfunc (s *quadSlice) WriteQuads(buf []quad.Quad) (int, error) {\n\t*s = append(*s, buf...)\n\treturn len(buf), nil\n}\n\nfunc TestWriteAsQuads(t *testing.T) {\n\tsch := schema.NewConfig()\n\tfor _, c := range testWriteValueCases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tvar out quadSlice\n\t\t\tid, err := sch.WriteAsQuads(&out, c.obj)\n\t\t\tif err != c.err {\n\t\t\t\tt.Errorf(\"unexpected error: %v (expected: %v)\", err, c.err)\n\t\t\t} else if c.err != nil {\n\t\t\t\treturn // case with expected error; omit other checks\n\t\t\t}\n\t\t\tif c.id == nil {\n\t\t\t\tfor i := range out {\n\t\t\t\t\tif c.expect[i].Subject == nil {\n\t\t\t\t\t\tc.expect[i].Subject = id\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if id != c.id {\n\t\t\t\tt.Errorf(\"ids are different: %v vs %v\", id, c.id)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual([]quad.Quad(out), c.expect) {\n\t\t\t\tt.Errorf(\"quad sets are different\\n%#v\\n%#v\", []quad.Quad(out), c.expect)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar testWriteValueCases = []struct {\n\tname   string\n\tobj    interface{}\n\tid     quad.Value\n\texpect []quad.Quad\n\terr    error\n}{\n\t{\n\t\t\"complex object\",\n\t\tstruct {\n\t\t\trdfType struct{} `quad:\"rdf:type > some:Type\"`\n\t\t\tID      quad.IRI `quad:\"@id\"`\n\t\t\tName    string   `quad:\"name\"`\n\t\t\tValues  []string `quad:\"values\"`\n\t\t\tItems   []item   `quad:\"items\"`\n\t\t\tSub     *item    `quad:\"sub\"`\n\t\t}{\n\t\t\tID:     \"1234\",\n\t\t\tName:   \"some item\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t\tItems: []item{\n\t\t\t\t{ID: \"sub1\", Name: \"Sub 1\"},\n\t\t\t\t{ID: \"sub2\", Name: \"Sub 2\"},\n\t\t\t},\n\t\t\tSub: &item{ID: \"sub3\", Name: \"Sub 3\"},\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(`some item`), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(`val1`), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(`val2`), nil},\n\n\t\t\t{iri(\"sub1\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub1\"), iri(\"name\"), quad.String(`Sub 1`), nil},\n\t\t\t{iri(\"1234\"), iri(\"items\"), iri(\"sub1\"), nil},\n\n\t\t\t{iri(\"sub2\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub2\"), iri(\"name\"), quad.String(`Sub 2`), nil},\n\t\t\t{iri(\"1234\"), iri(\"items\"), iri(\"sub2\"), nil},\n\n\t\t\t{iri(\"sub3\"), typeIRI, iri(\"some:item\"), nil},\n\t\t\t{iri(\"sub3\"), iri(\"name\"), quad.String(`Sub 3`), nil},\n\t\t\t{iri(\"1234\"), iri(\"sub\"), iri(\"sub3\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"complex object (embedded)\",\n\t\tstruct {\n\t\t\trdfType struct{} `quad:\"rdf:type > some:Type\"`\n\t\t\titem2\n\t\t\tID     quad.IRI `quad:\"@id\"`\n\t\t\tValues []string `quad:\"values\"`\n\t\t}{\n\t\t\titem2:  item2{Name: \"Sub 1\", Spec: \"special\"},\n\t\t\tID:     \"1234\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(`Sub 1`), nil},\n\t\t\t{iri(\"1234\"), iri(\"spec\"), quad.String(`special`), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(`val1`), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(`val2`), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"type shorthand\",\n\t\tstruct {\n\t\t\trdfType struct{} `quad:\"@type > some:Type\"`\n\t\t\titem2\n\t\t\tID     quad.IRI `quad:\"@id\"`\n\t\t\tValues []string `quad:\"values\"`\n\t\t}{\n\t\t\titem2:  item2{Name: \"Sub 1\", Spec: \"special\"},\n\t\t\tID:     \"1234\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"spec\"), quad.String(\"special\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"json tags\",\n\t\tstruct {\n\t\t\trdfType struct{} `quad:\"@type > some:Type\"`\n\t\t\titem2\n\t\t\tID     quad.IRI `json:\"@id\"`\n\t\t\tValues []string `json:\"values,omitempty\"`\n\t\t}{\n\t\t\titem2:  item2{Name: \"Sub 1\", Spec: \"special\"},\n\t\t\tID:     \"1234\",\n\t\t\tValues: []string{\"val1\", \"val2\"},\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), typeIRI, iri(\"some:Type\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Sub 1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"spec\"), quad.String(\"special\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val1\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"values\"), quad.String(\"val2\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"simple object\",\n\t\tsubObject{\n\t\t\tgenObject: genObject{\n\t\t\t\tID:   \"1234\",\n\t\t\t\tName: \"Obj\",\n\t\t\t},\n\t\t\tNum: 3,\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Obj\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"num\"), quad.Int(3), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"typedef\",\n\t\tgenObjectTypedef{\n\t\t\tID:   \"1234\",\n\t\t\tName: \"Obj\",\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Obj\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"simple object (embedded multiple levels)\",\n\t\tsubSubObject{\n\t\t\tsubObject: subObject{\n\t\t\t\tgenObject: genObject{\n\t\t\t\t\tID:   \"1234\",\n\t\t\t\t\tName: \"Obj\",\n\t\t\t\t},\n\t\t\t\tNum: 3,\n\t\t\t},\n\t\t\tNum2: 4,\n\t\t},\n\t\tiri(\"1234\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1234\"), iri(\"name\"), quad.String(\"Obj\"), nil},\n\t\t\t{iri(\"1234\"), iri(\"num\"), quad.Int(3), nil},\n\t\t\t{iri(\"1234\"), iri(\"num2\"), quad.Int(4), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"required field not set\",\n\t\titem2{Name: \"partial\"},\n\t\tnil, nil,\n\t\tschema.ErrReqFieldNotSet{Field: \"Spec\"},\n\t},\n\t{\n\t\t\"required unexported\",\n\t\ttimeItem{ID: \"1\", Name: \"t\", TS: time.Unix(100, 0)},\n\t\tnil,\n\t\t[]quad.Quad{\n\t\t\t{iri(\"1\"), iri(\"name\"), quad.String(\"t\"), nil},\n\t\t\t{iri(\"1\"), iri(\"ts\"), quad.Time(time.Unix(100, 0)), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"single tree node\",\n\t\ttreeItemOpt{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t},\n\t\tiri(\"n1\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"n1\"), iri(\"name\"), quad.String(\"Node 1\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"nested tree nodes\",\n\t\ttreeItemOpt{\n\t\t\tID:   iri(\"n1\"),\n\t\t\tName: \"Node 1\",\n\t\t\tChildren: []treeItemOpt{\n\t\t\t\t{ID: iri(\"n2\"), Name: \"Node 2\"},\n\t\t\t},\n\t\t},\n\t\tiri(\"n1\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"n1\"), iri(\"name\"), quad.String(\"Node 1\"), nil},\n\t\t\t{iri(\"n2\"), iri(\"name\"), quad.String(\"Node 2\"), nil},\n\t\t\t{iri(\"n1\"), iri(\"child\"), iri(\"n2\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"coords\",\n\t\tCoords{Lat: 12.3, Lng: 34.5},\n\t\tnil,\n\t\t[]quad.Quad{\n\t\t\t{nil, typeIRI, iri(\"ex:Coords\"), nil},\n\t\t\t{nil, iri(\"ex:lat\"), quad.Float(12.3), nil},\n\t\t\t{nil, iri(\"ex:lng\"), quad.Float(34.5), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"self loop\",\n\t\tfunc() *NodeLoop {\n\t\t\ta := &NodeLoop{ID: iri(\"A\"), Name: \"Node A\"}\n\t\t\ta.Next = a\n\t\t\treturn a\n\t\t}(),\n\t\tiri(\"A\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"A\"), iri(\"name\"), quad.String(\"Node A\"), nil},\n\t\t\t{iri(\"A\"), iri(\"next\"), iri(\"A\"), nil},\n\t\t},\n\t\tnil,\n\t},\n\t{\n\t\t\"pointer chain\",\n\t\tfunc() *NodeLoop {\n\t\t\ta := &NodeLoop{ID: iri(\"A\"), Name: \"Node A\"}\n\t\t\tb := &NodeLoop{ID: iri(\"B\"), Name: \"Node B\"}\n\t\t\tc := &NodeLoop{ID: iri(\"C\"), Name: \"Node C\"}\n\n\t\t\ta.Next = b\n\t\t\tb.Next = c\n\t\t\tc.Next = a\n\t\t\treturn a\n\t\t}(),\n\t\tiri(\"A\"),\n\t\t[]quad.Quad{\n\t\t\t{iri(\"A\"), iri(\"name\"), quad.String(\"Node A\"), nil},\n\t\t\t{iri(\"B\"), iri(\"name\"), quad.String(\"Node B\"), nil},\n\t\t\t{iri(\"C\"), iri(\"name\"), quad.String(\"Node C\"), nil},\n\t\t\t{iri(\"C\"), iri(\"next\"), iri(\"A\"), nil},\n\t\t\t{iri(\"B\"), iri(\"next\"), iri(\"C\"), nil},\n\t\t\t{iri(\"A\"), iri(\"next\"), iri(\"B\"), nil},\n\t\t},\n\t\tnil,\n\t},\n}\n"
  },
  {
    "path": "server/http/accept.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n//\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file or at\n// https://developers.google.com/open-source/licenses/bsd.\n\npackage cayleyhttp\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\n// Octet types from RFC 2616.\nvar octetTypes [256]octetType\n\ntype octetType byte\n\nconst (\n\tisToken octetType = 1 << iota\n\tisSpace\n)\n\nfunc init() {\n\t// OCTET      = <any 8-bit sequence of data>\n\t// CHAR       = <any US-ASCII character (octets 0 - 127)>\n\t// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n\t// CR         = <US-ASCII CR, carriage return (13)>\n\t// LF         = <US-ASCII LF, linefeed (10)>\n\t// SP         = <US-ASCII SP, space (32)>\n\t// HT         = <US-ASCII HT, horizontal-tab (9)>\n\t// <\">        = <US-ASCII double-quote mark (34)>\n\t// CRLF       = CR LF\n\t// LWS        = [CRLF] 1*( SP | HT )\n\t// TEXT       = <any OCTET except CTLs, but including LWS>\n\t// separators = \"(\" | \")\" | \"<\" | \">\" | \"@\" | \",\" | \";\" | \":\" | \"\\\" | <\">\n\t//              | \"/\" | \"[\" | \"]\" | \"?\" | \"=\" | \"{\" | \"}\" | SP | HT\n\t// token      = 1*<any CHAR except CTLs or separators>\n\t// qdtext     = <any TEXT except <\">>\n\n\tfor c := 0; c < 256; c++ {\n\t\tvar t octetType\n\t\tisCtl := c <= 31 || c == 127\n\t\tisChar := 0 <= c && c <= 127\n\t\tisSeparator := strings.IndexRune(\" \\t\\\"(),/:;<=>?@[]\\\\{}\", rune(c)) >= 0\n\t\tif strings.IndexRune(\" \\t\\r\\n\", rune(c)) >= 0 {\n\t\t\tt |= isSpace\n\t\t}\n\t\tif isChar && !isCtl && !isSeparator {\n\t\t\tt |= isToken\n\t\t}\n\t\toctetTypes[c] = t\n\t}\n}\n\n// AcceptSpec describes an Accept* header.\ntype AcceptSpec struct {\n\tValue string\n\tQ     float64\n}\n\n// ParseAccept parses Accept* headers.\nfunc ParseAccept(header http.Header, key string) (specs []AcceptSpec) {\nloop:\n\tfor _, s := range header[key] {\n\t\tfor {\n\t\t\tvar spec AcceptSpec\n\t\t\tspec.Value, s = expectTokenSlash(s)\n\t\t\tif spec.Value == \"\" {\n\t\t\t\tcontinue loop\n\t\t\t}\n\t\t\tspec.Q = 1.0\n\t\t\ts = skipSpace(s)\n\t\t\tif strings.HasPrefix(s, \";\") {\n\t\t\t\ts = skipSpace(s[1:])\n\t\t\t\tif !strings.HasPrefix(s, \"q=\") {\n\t\t\t\t\tcontinue loop\n\t\t\t\t}\n\t\t\t\tspec.Q, s = expectQuality(s[2:])\n\t\t\t\tif spec.Q < 0.0 {\n\t\t\t\t\tcontinue loop\n\t\t\t\t}\n\t\t\t}\n\t\t\tspecs = append(specs, spec)\n\t\t\ts = skipSpace(s)\n\t\t\tif !strings.HasPrefix(s, \",\") {\n\t\t\t\tcontinue loop\n\t\t\t}\n\t\t\ts = skipSpace(s[1:])\n\t\t}\n\t}\n\treturn\n}\n\nfunc skipSpace(s string) (rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isSpace == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[i:]\n}\n\nfunc expectTokenSlash(s string) (token, rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tb := s[i]\n\t\tif (octetTypes[b]&isToken == 0) && b != '/' {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[:i], s[i:]\n}\n\nfunc expectQuality(s string) (q float64, rest string) {\n\tswitch {\n\tcase len(s) == 0:\n\t\treturn -1, \"\"\n\tcase s[0] == '0':\n\t\tq = 0\n\tcase s[0] == '1':\n\t\tq = 1\n\tdefault:\n\t\treturn -1, \"\"\n\t}\n\ts = s[1:]\n\tif !strings.HasPrefix(s, \".\") {\n\t\treturn q, s\n\t}\n\ts = s[1:]\n\ti := 0\n\tn := 0\n\td := 1\n\tfor ; i < len(s); i++ {\n\t\tb := s[i]\n\t\tif b < '0' || b > '9' {\n\t\t\tbreak\n\t\t}\n\t\tn = n*10 + int(b) - '0'\n\t\td *= 10\n\t}\n\treturn q + float64(n)/float64(d), s[i:]\n}\n"
  },
  {
    "path": "server/http/api_v2.go",
    "content": "// Copyright 2017 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cayleyhttp\n\nimport (\n\t\"compress/gzip\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\n\t\"github.com/cayleygraph/cayley/clog\"\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/query\"\n\t\"github.com/cayleygraph/cayley/query/shape\"\n\n\t// Writer is imported for writers to be registered\n\t_ \"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/voc\"\n)\n\nconst (\n\tprefix             = \"/api/v2\"\n\tdefaultLimit       = 100\n\tdefaultReplication = \"single\"\n)\n\n// NewAPIv2 creates a new instance of APIv2 with default options\nfunc NewAPIv2(h *graph.Handle, wrappers ...HandlerWrapper) *APIv2 {\n\treturn NewAPIv2Writer(h, defaultReplication, nil, wrappers...)\n}\n\n// NewBoundAPIv2 creates a new instance of APIv2 bound to a given httprouter.Router\nfunc NewBoundAPIv2(h *graph.Handle, r *httprouter.Router) *APIv2 {\n\tapi := &APIv2{h: h, wtyp: defaultReplication, wopt: nil, limit: defaultLimit, handler: r}\n\tapi.registerOn(r)\n\treturn api\n}\n\n// NewAPIv2Writer creates a new instance of APIv2\nfunc NewAPIv2Writer(h *graph.Handle, wtype string, wopts graph.Options, wrappers ...HandlerWrapper) *APIv2 {\n\tr := httprouter.New()\n\tapi := &APIv2{h: h, wtyp: wtype, wopt: wopts, limit: defaultLimit}\n\tapi.registerOn(r)\n\tvar handler http.Handler = r\n\tfor _, wrapper := range wrappers {\n\t\thandler = wrapper(handler)\n\t}\n\tapi.handler = handler\n\treturn api\n}\n\n// APIv2 holds state and configuration of a request\ntype APIv2 struct {\n\th       *graph.Handle\n\tro      bool\n\tbatch   int\n\thandler http.Handler\n\n\t// replication\n\twtyp string\n\twopt graph.Options\n\n\t// query\n\ttimeout time.Duration\n\tlimit   int\n}\n\n// SetReadOnly sets read-only mode for the request\nfunc (api *APIv2) SetReadOnly(ro bool) {\n\tapi.ro = ro\n}\n\n// SetBatchSize sets batch-size mode for the request\nfunc (api *APIv2) SetBatchSize(n int) {\n\tapi.batch = n\n}\n\n// SetQueryTimeout sets query timeout for the request\nfunc (api *APIv2) SetQueryTimeout(dt time.Duration) {\n\tapi.timeout = dt\n}\n\n// SetQueryLimit sets query limit for the request\nfunc (api *APIv2) SetQueryLimit(n int) {\n\tapi.limit = n\n}\n\n// ServeHTTP implements http.Handler\nfunc (api *APIv2) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tapi.handler.ServeHTTP(w, r)\n}\n\n// HandlerWrapper accepts a handler, wraps it with additional functionality and\n// returns a new handler\ntype HandlerWrapper func(http.Handler) http.Handler\n\n// toHandle wraps a http.HandlerFunc to comply with the signature of httprouter.Handle\nfunc toHandle(handler http.HandlerFunc) httprouter.Handle {\n\treturn func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {\n\t\thandler(w, r)\n\t}\n}\n\nfunc (api *APIv2) registerDataOn(r *httprouter.Router) {\n\tif !api.ro {\n\t\tr.POST(prefix+\"/write\", toHandle(api.ServeWrite))\n\t\tr.POST(prefix+\"/delete\", toHandle(api.ServeDelete))\n\t\tr.POST(prefix+\"/node/delete\", toHandle(api.ServeNodeDelete))\n\t}\n\tr.POST(prefix+\"/read\", toHandle(api.ServeRead))\n\tr.GET(prefix+\"/read\", toHandle(api.ServeRead))\n\tr.GET(prefix+\"/formats\", toHandle(api.ServeFormats))\n}\n\nfunc (api *APIv2) registerQueryOn(r *httprouter.Router) {\n\tr.POST(prefix+\"/query\", toHandle(api.ServeQuery))\n\tr.GET(prefix+\"/query\", toHandle(api.ServeQuery))\n}\n\nfunc (api *APIv2) registerOn(r *httprouter.Router) {\n\tapi.registerDataOn(r)\n\tapi.registerQueryOn(r)\n}\n\nconst (\n\tdefaultFormat      = \"nquads\"\n\thdrContentType     = \"Content-Type\"\n\thdrContentEncoding = \"Content-Encoding\"\n\thdrAccept          = \"Accept\"\n\thdrAcceptEncoding  = \"Accept-Encoding\"\n\tcontentTypeJSON    = \"application/json\"\n\tcontentTypeJSONLD  = \"application/ld+json\"\n)\n\nfunc getFormat(r *http.Request, formKey string, acceptName string) *quad.Format {\n\tvar format *quad.Format\n\tif formKey != \"\" {\n\t\tif name := r.FormValue(\"format\"); name != \"\" {\n\t\t\tformat = quad.FormatByName(name)\n\t\t}\n\t}\n\tif acceptName != \"\" && format == nil {\n\t\tspecs := ParseAccept(r.Header, acceptName)\n\t\t// TODO: sort by Q\n\t\tif len(specs) != 0 {\n\t\t\tformat = quad.FormatByMime(specs[0].Value)\n\t\t}\n\t}\n\tif format == nil {\n\t\tformat = quad.FormatByName(defaultFormat)\n\t}\n\treturn format\n}\n\nfunc readerFrom(r *http.Request, acceptName string) (io.ReadCloser, error) {\n\tif specs := ParseAccept(r.Header, acceptName); len(specs) != 0 {\n\t\tif s := specs[0]; s.Value == \"gzip\" {\n\t\t\tzr, err := gzip.NewReader(r.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn zr, nil\n\t\t}\n\t}\n\treturn r.Body, nil\n}\n\ntype nopWriteCloser struct {\n\tio.Writer\n}\n\nfunc (nopWriteCloser) Close() error { return nil }\n\nfunc writerFrom(w http.ResponseWriter, r *http.Request, acceptName string) io.WriteCloser {\n\tif specs := ParseAccept(r.Header, acceptName); len(specs) != 0 {\n\t\tif s := specs[0]; s.Value == \"gzip\" {\n\t\t\tw.Header().Set(hdrContentEncoding, s.Value)\n\t\t\tzw := gzip.NewWriter(w)\n\t\t\treturn zw\n\t\t}\n\t}\n\treturn nopWriteCloser{Writer: w}\n}\n\nfunc (api *APIv2) handleForRequest(r *http.Request) (*graph.Handle, error) {\n\treturn HandleForRequest(api.h, api.wtyp, api.wopt, r)\n}\n\n// writeResponse represents the response received for a successful write\ntype writeResponse struct {\n\tResult string `json:\"result\"`\n\tCount  int    `json:\"count\"`\n}\n\n// newWriteResponse creates a new WriteResponse for given count of quads written\nfunc newWriteResponse(count int) writeResponse {\n\treturn writeResponse{\n\t\tResult: fmt.Sprintf(\"Successfully wrote %d quads.\", count),\n\t\tCount:  count,\n\t}\n}\n\n// ServeWrite writes data received in the request body to the database\nfunc (api *APIv2) ServeWrite(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\tif api.ro {\n\t\tjsonResponse(w, http.StatusForbidden, errors.New(\"database is read-only\"))\n\t\treturn\n\t}\n\tformat := getFormat(r, \"\", hdrContentType)\n\tif format == nil || format.Reader == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, errors.New(\"format is not supported for reading data\"))\n\t\treturn\n\t}\n\trd, err := readerFrom(r, hdrContentEncoding)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\tdefer rd.Close()\n\tqr := format.Reader(rd)\n\tdefer qr.Close()\n\th, err := api.handleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\tqw := graph.NewWriter(h.QuadWriter)\n\tdefer qw.Close()\n\tn, err := quad.CopyBatch(qw, qr, api.batch)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusInternalServerError, err)\n\t\treturn\n\t}\n\terr = qw.Close()\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusInternalServerError, err)\n\t\treturn\n\t}\n\tw.Header().Set(hdrContentType, contentTypeJSON)\n\tresponse := newWriteResponse(n)\n\tencoder := json.NewEncoder(w)\n\tencoder.Encode(response)\n}\n\n// ServeDelete deletes data received in the request body from the database.\n// Responds with how many quads were deleted.\nfunc (api *APIv2) ServeDelete(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\tif api.ro {\n\t\tjsonResponse(w, http.StatusForbidden, errors.New(\"database is read-only\"))\n\t\treturn\n\t}\n\tformat := getFormat(r, \"\", hdrContentType)\n\tif format == nil || format.Reader == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, fmt.Errorf(\"format is not supported for reading quads\"))\n\t\treturn\n\t}\n\trd, err := readerFrom(r, hdrContentEncoding)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\tdefer rd.Close()\n\tqr := format.Reader(r.Body)\n\tdefer qr.Close()\n\th, err := api.handleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\tqw := graph.NewRemover(h.QuadWriter)\n\tdefer qw.Close()\n\tn, err := quad.CopyBatch(qw, qr, api.batch)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusInternalServerError, err)\n\t\treturn\n\t}\n\tw.Header().Set(hdrContentType, contentTypeJSON)\n\tfmt.Fprintf(w, `{\"result\": \"Successfully deleted %d quads.\", \"count\": %d}`+\"\\n\", n, n)\n}\n\n// ServeNodeDelete deletes all data associated with a node (an entity).\n// Responds with how many quads were deleted.\nfunc (api *APIv2) ServeNodeDelete(w http.ResponseWriter, r *http.Request) {\n\tdefer r.Body.Close()\n\tif api.ro {\n\t\tjsonResponse(w, http.StatusForbidden, errors.New(\"database is read-only\"))\n\t\treturn\n\t}\n\tformat := getFormat(r, \"\", hdrContentType)\n\tif format == nil || format.UnmarshalValue == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, fmt.Errorf(\"format is not supported for reading nodes\"))\n\t\treturn\n\t}\n\tconst limit = 128*1024 + 1\n\trd := io.LimitReader(r.Body, limit)\n\tdata, err := ioutil.ReadAll(rd)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t} else if len(data) == limit {\n\t\tjsonResponse(w, http.StatusBadRequest, fmt.Errorf(\"request data is too large\"))\n\t\treturn\n\t}\n\tv, err := format.UnmarshalValue(data)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t} else if v == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, fmt.Errorf(\"cannot remove nil value\"))\n\t\treturn\n\t}\n\th, err := api.handleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\terr = h.RemoveNode(v)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusInternalServerError, err)\n\t\treturn\n\t}\n\tw.Header().Set(hdrContentType, contentTypeJSON)\n\tconst n = 1\n\tfmt.Fprintf(w, `{\"result\": \"Successfully deleted %d nodes.\", \"count\": %d}`+\"\\n\", n, n)\n}\n\ntype checkWriter struct {\n\tw       io.Writer\n\twritten bool\n}\n\nfunc (w *checkWriter) Write(p []byte) (int, error) {\n\tw.written = true\n\treturn w.w.Write(p)\n}\n\nfunc valuesFromString(s string) []quad.Value {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\tarr := strings.Split(s, \",\")\n\tout := make([]quad.Value, 0, len(arr))\n\tfor _, s := range arr {\n\t\tout = append(out, quad.StringToValue(s))\n\t}\n\treturn out\n}\n\n// ServeRead responds with quads read from the database\nfunc (api *APIv2) ServeRead(w http.ResponseWriter, r *http.Request) {\n\tformat := getFormat(r, \"format\", hdrAccept)\n\tif format == nil || format.Writer == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, fmt.Errorf(\"format is not supported for reading data\"))\n\t\treturn\n\t}\n\th, err := api.handleForRequest(r)\n\tif err != nil {\n\t\tjsonResponse(w, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\tvalues := shape.FilterQuads(\n\t\tvaluesFromString(r.FormValue(\"sub\")),\n\t\tvaluesFromString(r.FormValue(\"pred\")),\n\t\tvaluesFromString(r.FormValue(\"obj\")),\n\t\tvaluesFromString(r.FormValue(\"label\")),\n\t)\n\tit := values.BuildIterator(h.QuadStore).Iterate()\n\tqr := graph.NewResultReader(h.QuadStore, it)\n\n\tdefer qr.Close()\n\n\twr := writerFrom(w, r, hdrAcceptEncoding)\n\tdefer wr.Close()\n\n\tcw := &checkWriter{w: wr}\n\tqwc := format.Writer(cw)\n\tdefer qwc.Close()\n\tvar qw quad.Writer = qwc\n\tif len(format.Mime) != 0 {\n\t\tw.Header().Set(hdrContentType, format.Mime[0])\n\t}\n\tif irif := r.FormValue(\"iri\"); irif != \"\" {\n\t\topts := quad.IRIOptions{\n\t\t\tFormat: quad.IRIDefault,\n\t\t}\n\t\tswitch irif {\n\t\tcase \"short\":\n\t\t\topts.Format = quad.IRIShort\n\t\tcase \"full\":\n\t\t\topts.Format = quad.IRIFull\n\t\t}\n\t\tqw = quad.IRIWriter(qw, opts)\n\t}\n\tif bw, ok := qw.(quad.BatchWriter); ok {\n\t\t_, err = quad.CopyBatch(bw, qr, api.batch)\n\t} else {\n\t\t_, err = quad.Copy(qw, qr)\n\t}\n\tif err != nil && !cw.written {\n\t\tjsonResponse(w, http.StatusInternalServerError, err)\n\t\treturn\n\t} else if err != nil {\n\t\t// can do nothing here, since first byte (and header) was written\n\t\t// TODO: check if client just gone away\n\t\tclog.Errorf(\"read quads error: %v\", err)\n\t}\n}\n\n// ServeFormats responds with formats known for the database\nfunc (api *APIv2) ServeFormats(w http.ResponseWriter, r *http.Request) {\n\ttype Format struct {\n\t\tID     string   `json:\"id\"`\n\t\tRead   bool     `json:\"read,omitempty\"`\n\t\tWrite  bool     `json:\"write,omitempty\"`\n\t\tNodes  bool     `json:\"nodes,omitempty\"`\n\t\tExt    []string `json:\"ext,omitempty\"`\n\t\tMime   []string `json:\"mime,omitempty\"`\n\t\tBinary bool     `json:\"binary,omitempty\"`\n\t}\n\tformats := quad.Formats()\n\tout := make([]Format, 0, len(formats))\n\tfor _, f := range formats {\n\t\tout = append(out, Format{\n\t\t\tID:  f.Name,\n\t\t\tExt: f.Ext, Mime: f.Mime,\n\t\t\tRead: f.Reader != nil, Write: f.Writer != nil,\n\t\t\tNodes:  f.UnmarshalValue != nil,\n\t\t\tBinary: f.Binary,\n\t\t})\n\t}\n\tw.Header().Set(hdrContentType, contentTypeJSON)\n\tjson.NewEncoder(w).Encode(out)\n}\n\nfunc (api *APIv2) queryContext(r *http.Request) (ctx context.Context, cancel func()) {\n\tctx = r.Context()\n\tif api.timeout > 0 {\n\t\tctx, cancel = context.WithTimeout(ctx, api.timeout)\n\t} else {\n\t\tctx, cancel = context.WithCancel(ctx)\n\t}\n\treturn ctx, cancel\n}\n\nfunc defaultErrorFunc(w query.ResponseWriter, err error) {\n\tdata, _ := json.Marshal(err.Error())\n\tw.WriteHeader(http.StatusBadRequest)\n\tw.Write([]byte(`{\"error\": `))\n\tw.Write(data)\n\tw.Write([]byte(\"}\\n\"))\n}\n\nfunc writeResults(w io.Writer, r interface{}) {\n\tenc := json.NewEncoder(w)\n\tenc.SetEscapeHTML(false)\n\tenc.Encode(map[string]interface{}{\n\t\t\"result\": r,\n\t})\n}\n\nconst maxQuerySize = 1024 * 1024 // 1 MB\nfunc readLimit(r io.Reader) ([]byte, error) {\n\tlr := io.LimitReader(r, maxQuerySize).(*io.LimitedReader)\n\tdata, err := ioutil.ReadAll(lr)\n\tif err != nil && lr.N <= 0 {\n\t\terr = errors.New(\"request is too large\")\n\t}\n\treturn data, err\n}\n\n// ServeQuery executes a query received in the request and responds with the result\nfunc (api *APIv2) ServeQuery(w http.ResponseWriter, r *http.Request) {\n\tctx, cancel := api.queryContext(r)\n\tdefer cancel()\n\tvals := r.URL.Query()\n\tlang := vals.Get(\"lang\")\n\tif lang == \"\" {\n\t\tjsonResponse(w, http.StatusBadRequest, \"query language not specified\")\n\t\treturn\n\t}\n\tl := query.GetLanguage(lang)\n\tif l == nil {\n\t\tjsonResponse(w, http.StatusBadRequest, \"unknown query language\")\n\t\treturn\n\t}\n\terrFunc := defaultErrorFunc\n\tif l.HTTPError != nil {\n\t\terrFunc = l.HTTPError\n\t}\n\tselect {\n\tcase <-ctx.Done():\n\t\terrFunc(w, ctx.Err())\n\t\treturn\n\tdefault:\n\t}\n\th, err := api.handleForRequest(r)\n\tif err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tif l.HTTPQuery != nil {\n\t\tdefer r.Body.Close()\n\t\tl.HTTPQuery(ctx, h.QuadStore, w, r.Body)\n\t\treturn\n\t}\n\tif l.Session == nil {\n\t\terrFunc(w, errors.New(\"HTTP interface is not supported for this query language\"))\n\t\treturn\n\t}\n\tses := l.Session(h.QuadStore)\n\tvar qu string\n\tif r.Method == \"GET\" {\n\t\tqu = vals.Get(\"qu\")\n\t} else {\n\t\tdata, err := readLimit(r.Body)\n\t\tif err != nil {\n\t\t\terrFunc(w, err)\n\t\t\treturn\n\t\t}\n\t\tqu = string(data)\n\t}\n\tif qu == \"\" {\n\t\tjsonResponse(w, http.StatusBadRequest, \"query is empty\")\n\t\treturn\n\t}\n\tif clog.V(1) {\n\t\tclog.Infof(\"query: %s: %q\", lang, qu)\n\t}\n\n\topt := query.Options{\n\t\tCollation: query.JSON, // TODO: switch to JSON-LD by default when the time comes\n\t\tLimit:     api.limit,\n\t}\n\tif specs := ParseAccept(r.Header, hdrAccept); len(specs) != 0 {\n\t\t// TODO: sort by Q\n\t\tswitch specs[0].Value {\n\t\tcase contentTypeJSON:\n\t\t\topt.Collation = query.JSON\n\t\tcase contentTypeJSONLD:\n\t\t\topt.Collation = query.JSONLD\n\t\t}\n\t}\n\tit, err := ses.Execute(ctx, qu, opt)\n\tif err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tdefer it.Close()\n\n\tvar out []interface{}\n\tfor it.Next(ctx) {\n\t\tout = append(out, it.Result())\n\t}\n\tif err = it.Err(); err != nil {\n\t\terrFunc(w, err)\n\t\treturn\n\t}\n\tif opt.Collation == query.JSONLD {\n\t\tw.Header().Set(hdrContentType, contentTypeJSONLD)\n\t} else {\n\t\tw.Header().Set(hdrContentType, contentTypeJSON)\n\t}\n\twriteResults(w, out)\n}\n\n// NamespaceRule defines a prefix for a namespace when prepended to the suffix of a compact IRI, results in an IRI.\n// For example rdfs is a prefix for the namespace http://www.w3.org/2000/01/rdf-schema#.\ntype NamespaceRule struct {\n\tPrefix    string `json:\"prefix\"`\n\tNamespace string `json:\"namespace\"`\n}\n\n// getNamespaceRules returns all the registered rules\nfunc getNamespaceRules() []NamespaceRule {\n\tvar rules []NamespaceRule\n\tfor _, n := range voc.List() {\n\t\trules = append(rules, NamespaceRule{\n\t\t\tPrefix:    strings.TrimSuffix(n.Prefix, \":\"),\n\t\t\tNamespace: n.Full,\n\t\t})\n\t}\n\treturn rules\n}\n\n// serveGetNamespaceRules responds with all the registered rules encoded to JSON\nfunc serveGetNamespaceRules(w http.ResponseWriter) {\n\trules := getNamespaceRules()\n\tencoder := json.NewEncoder(w)\n\tw.Header().Set(hdrContentType, contentTypeJSON)\n\tw.WriteHeader(http.StatusOK)\n\tencoder.Encode(rules)\n}\n\n// serveGetNamespaceRules registers received rule encoded in JSON\nfunc servePostNamespaceRules(w http.ResponseWriter, r *http.Request) {\n\tvar rule NamespaceRule\n\tdecoder := json.NewDecoder(r.Body)\n\tdecoder.Decode(&rule)\n\tvoc.RegisterPrefix(rule.Prefix, rule.Namespace)\n\tw.WriteHeader(http.StatusCreated)\n}\n\n// ServeNamespaceRules serves requests for the namespace rules resource.\n// The resource supports getting all registered rules and registering a rule.\n// The resource wraps the quad/voc module.\nfunc (api *APIv2) ServeNamespaceRules(w http.ResponseWriter, r *http.Request) {\n\tswitch r.Method {\n\tcase http.MethodGet:\n\t\tserveGetNamespaceRules(w)\n\t\treturn\n\tcase http.MethodPost:\n\t\tservePostNamespaceRules(w, r)\n\t\treturn\n\tdefault:\n\t\tjsonResponse(w, http.StatusMethodNotAllowed, nil)\n\t}\n}\n"
  },
  {
    "path": "server/http/api_v2_test.go",
    "content": "package cayleyhttp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/cayley/graph/memstore\"\n\t\"github.com/cayleygraph/cayley/writer\"\n\t\"github.com/cayleygraph/quad\"\n\t\"github.com/cayleygraph/quad/jsonld\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc makeHandle(t testing.TB, quads ...quad.Quad) *graph.Handle {\n\tqs := memstore.New(quads...)\n\twr, err := writer.NewSingleReplication(qs, nil)\n\trequire.NoError(t, err)\n\treturn &graph.Handle{\n\t\tQuadStore:  qs,\n\t\tQuadWriter: wr,\n\t}\n}\n\nfunc makeServerV2(t testing.TB, quads ...quad.Quad) *APIv2 {\n\th := makeHandle(t, quads...)\n\treturn NewAPIv2(h)\n}\n\nfunc writeQuads(q []quad.Quad, w io.Writer) error {\n\twriter := jsonld.NewWriter(w)\n\treader := quad.NewReader(quads)\n\t_, err := quad.Copy(writer, reader)\n\twriter.Close()\n\treturn err\n}\n\nfunc newQuadsBuffer(quads []quad.Quad) (*bytes.Buffer, error) {\n\tbuf := bytes.NewBuffer(nil)\n\terr := writeQuads(quads, buf)\n\treturn buf, err\n}\n\nvar mime = quad.FormatByName(\"jsonld\").Mime[0]\n\nvar quads = []quad.Quad{\n\tquad.MakeIRI(\"http://example.com/bob\", \"http://example.com/likes\", \"http://example.com/alice\", \"\"),\n\tquad.MakeIRI(\"http://example.com/alice\", \"http://example.com/likes\", \"http://example.com/bob\", \"\"),\n}\n\nfunc TestV2Write(t *testing.T) {\n\tapi := makeServerV2(t)\n\tbuf, err := newQuadsBuffer(quads)\n\n\treq, err := http.NewRequest(http.MethodGet, prefix+\"/write\", buf)\n\trequire.NoError(t, err)\n\treq.Header.Set(hdrContentType, mime)\n\n\trr := httptest.NewRecorder()\n\thandler := http.HandlerFunc(api.ServeWrite)\n\thandler.ServeHTTP(rr, req)\n\n\trequire.Equal(t, http.StatusOK, rr.Code, rr.Body.String())\n\n\texpectedResponse := newWriteResponse(len(quads))\n\n\tvar response writeResponse\n\tjson.Unmarshal(rr.Body.Bytes(), &response)\n\n\trequire.Equal(t, expectedResponse, response)\n}\n\nfunc TestV2Read(t *testing.T) {\n\tapi := makeServerV2(t, quads...)\n\tbuf := bytes.NewBuffer(nil)\n\n\treq, err := http.NewRequest(http.MethodGet, prefix+\"/read\", buf)\n\trequire.NoError(t, err)\n\treq.Header.Set(hdrAccept, mime)\n\n\trr := httptest.NewRecorder()\n\thandler := http.HandlerFunc(api.ServeRead)\n\thandler.ServeHTTP(rr, req)\n\n\trequire.Equal(t, http.StatusOK, rr.Code, rr.Body.String())\n\n\treader := jsonld.NewReader(rr.Body)\n\treceivedQuads, err := quad.ReadAll(reader)\n\trequire.NoError(t, err)\n\tsort.Sort(quad.ByQuadString(receivedQuads))\n\tsort.Sort(quad.ByQuadString(quads))\n\trequire.Equal(t, quads, receivedQuads)\n\n}\n\nfunc TestV2Delete(t *testing.T) {\n\tapi := makeServerV2(t, quads...)\n\tbuf, err := newQuadsBuffer(quads)\n\trequire.NoError(t, err)\n\n\treq, err := http.NewRequest(http.MethodGet, prefix+\"/delete\", buf)\n\trequire.NoError(t, err)\n\treq.Header.Set(hdrContentType, mime)\n\n\trr := httptest.NewRecorder()\n\thandler := http.HandlerFunc(api.ServeDelete)\n\thandler.ServeHTTP(rr, req)\n\n\trequire.Equal(t, http.StatusOK, rr.Code, rr.Body.String())\n}\n\nfunc TestV2GetNamespaceRules(t *testing.T) {\n\tapi := makeServerV2(t)\n\tbuf := bytes.NewBuffer(nil)\n\treq, err := http.NewRequest(http.MethodGet, prefix+\"/namespace-rules\", buf)\n\trequire.NoError(t, err)\n\trr := httptest.NewRecorder()\n\thandler := http.HandlerFunc(api.ServeNamespaceRules)\n\thandler.ServeHTTP(rr, req)\n\tvar rules []NamespaceRule\n\terr = json.Unmarshal(rr.Body.Bytes(), &rules)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusOK, rr.Code)\n\trequire.Equal(t, contentTypeJSON, rr.Header().Get(hdrContentType))\n\trequire.NotEmpty(t, rules)\n}\n\nfunc TestV2PostNamespaceRules(t *testing.T) {\n\tapi := makeServerV2(t)\n\trule := NamespaceRule{\n\t\tPrefix:    \"foaf\",\n\t\tNamespace: \"http://xmlns.com/foaf/0.1/\",\n\t}\n\tbuf := bytes.NewBuffer(nil)\n\tencoder := json.NewEncoder(buf)\n\tencoder.Encode(rule)\n\treq, err := http.NewRequest(http.MethodPost, prefix+\"/namespace-rules\", buf)\n\trequire.NoError(t, err)\n\trr := httptest.NewRecorder()\n\thandler := http.HandlerFunc(api.ServeNamespaceRules)\n\thandler.ServeHTTP(rr, req)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, rr.Code)\n\trequire.Empty(t, rr.Body.Bytes())\n\n\t// Check effect\n\treq, err = http.NewRequest(http.MethodGet, prefix+\"/namespace-rules\", buf)\n\trequire.NoError(t, err)\n\trr = httptest.NewRecorder()\n\thandler = http.HandlerFunc(api.ServeNamespaceRules)\n\thandler.ServeHTTP(rr, req)\n\tvar rules []NamespaceRule\n\terr = json.Unmarshal(rr.Body.Bytes(), &rules)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusOK, rr.Code)\n\trequire.Equal(t, contentTypeJSON, rr.Header().Get(hdrContentType))\n\trequire.Contains(t, rules, rule)\n}\n"
  },
  {
    "path": "server/http/common.go",
    "content": "package cayleyhttp\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/cayleygraph/cayley/graph\"\n\thttpgraph \"github.com/cayleygraph/cayley/graph/http\"\n)\n\nfunc jsonResponse(w http.ResponseWriter, code int, err interface{}) {\n\tw.Header().Set(\"Content-Type\", contentTypeJSON)\n\tw.WriteHeader(code)\n\tw.Write([]byte(`{\"error\": `))\n\tvar s string\n\tswitch err := err.(type) {\n\tcase string:\n\t\ts = err\n\tcase error:\n\t\ts = err.Error()\n\tdefault:\n\t\ts = fmt.Sprint(err)\n\t}\n\tdata, _ := json.Marshal(s)\n\tw.Write(data)\n\tw.Write([]byte(`}`))\n}\n\n// HandleForRequest returns new graph.Handle for given writer name, options and request\nfunc HandleForRequest(h *graph.Handle, wtyp string, wopt graph.Options, r *http.Request) (*graph.Handle, error) {\n\tg, ok := h.QuadStore.(httpgraph.QuadStore)\n\tif !ok {\n\t\treturn h, nil\n\t}\n\tqs, err := g.ForRequest(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tqw, err := graph.NewQuadWriter(wtyp, qs, wopt)\n\tif err != nil {\n\t\tqs.Close()\n\t\treturn nil, err\n\t}\n\treturn &graph.Handle{QuadStore: qs, QuadWriter: qw}, nil\n}\n"
  },
  {
    "path": "ui/embed.go",
    "content": "package ui\n\nimport \"embed\"\n\n//go:embed web\nvar FS embed.FS\n"
  },
  {
    "path": "ui/web/asset-manifest.json",
    "content": "{\n  \"files\": {\n    \"main.css\": \"/static/css/main.72fe53e6.chunk.css\",\n    \"main.js\": \"/static/js/main.84d3ab8c.chunk.js\",\n    \"main.js.map\": \"/static/js/main.84d3ab8c.chunk.js.map\",\n    \"runtime-main.js\": \"/static/js/runtime-main.0686c6e7.js\",\n    \"runtime-main.js.map\": \"/static/js/runtime-main.0686c6e7.js.map\",\n    \"static/css/2.6b51f286.chunk.css\": \"/static/css/2.6b51f286.chunk.css\",\n    \"static/js/2.7d84b3fa.chunk.js\": \"/static/js/2.7d84b3fa.chunk.js\",\n    \"static/js/2.7d84b3fa.chunk.js.map\": \"/static/js/2.7d84b3fa.chunk.js.map\",\n    \"index.html\": \"/index.html\",\n    \"precache-manifest.5316e0e4a35813e95b82d4799f1bb55c.js\": \"/precache-manifest.5316e0e4a35813e95b82d4799f1bb55c.js\",\n    \"service-worker.js\": \"/service-worker.js\",\n    \"static/css/2.6b51f286.chunk.css.map\": \"/static/css/2.6b51f286.chunk.css.map\",\n    \"static/css/main.72fe53e6.chunk.css.map\": \"/static/css/main.72fe53e6.chunk.css.map\",\n    \"static/media/logo.svg\": \"/static/media/logo.ca711468.svg\"\n  },\n  \"entrypoints\": [\n    \"static/js/runtime-main.0686c6e7.js\",\n    \"static/css/2.6b51f286.chunk.css\",\n    \"static/js/2.7d84b3fa.chunk.js\",\n    \"static/css/main.72fe53e6.chunk.css\",\n    \"static/js/main.84d3ab8c.chunk.js\"\n  ]\n}"
  },
  {
    "path": "ui/web/gizmo.d.ts",
    "content": "/**\n * Both `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods.\n * Note that `.Vertex()` returns a query object, which is a subclass of path object.\n *\n * For these examples, suppose we have the following graph:\n *\n * ```\n * +-------+                        +------+\n * | alice |-----                 ->| fred |<--\n * +-------+     \\---->+-------+-/  +------+   \\-+-------+\n *               ----->| #bob# |       |         |*emily*|\n * +---------+--/  --->+-------+       |         +-------+\n * | charlie |    /                    v\n * +---------+   /                  +--------+\n *   \\---    +--------+             |*#greg#*|\n *       \\-->| #dani# |------------>+--------+\n *           +--------+\n * ```\n *\n * Where every link is a `<follows>` relationship, and the nodes with an extra `#` in the name have an extra `<status>` link. As in,\n *\n * ```\n * <dani> -- <status> --> \"cool_person\"\n * ```\n *\n * Perhaps these are the influencers in our community. So too are extra `*`s in the name -- these are our smart people,\n * according to the `<smart_graph>` label, eg, the quad:\n *\n * ```\n * <greg> <status> \"smart_person\" <smart_graph> .\n * ```\n */\ninterface Path {\n  /** Execute the query and adds the results, with all tags, as a string-to-string (tag to node) map in the output set, one for each path that a traversal could take. */\n  all(): void;\n  /** Alias for intersect. */\n  and(path: Path): Path;\n  /** Alias for tag. */\n  as(...tags: string[]): Path;\n  /** Return current path to a set of nodes on a given tag, preserving all constraints.\n   * If still valid, a path will now consider their vertex to be the same one as the previously tagged one, with the added constraint that it was valid all the way here. Useful for traversing back in queries and taking another route for things that have matched so far. */\n  back(tag?: string): Path;\n  /** Follow the predicate in either direction. Same as out or in. */\n  both(path: Path, ...tags: string[]): Path;\n  /** Return a number of results and returns it as a value. */\n  count(): number;\n  /** Alias for Except */\n  difference(path: Path): Path;\n  /** Removes all paths which match query from current path. In a set-theoretic sense, this is (A - B). While `g.V().Except(path)` to achieve `U - B = !B` is supported, it's often very slow. */\n  except(path: Path): Path;\n  /** Apply constraints to a set of nodes. Can be used to filter values by range or match strings. */\n  filter(...args: any): Path;\n  /** The way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path.\n   * Starts as if at the g.M() and follows through the morphism path. */\n  follow(path: Path): Path;\n  /** The same as follow but follows the chain in the reverse direction. Flips \"In\" and \"Out\" where appropriate,\nthe net result being a virtual predicate followed in the reverse direction. Starts at the end of the morphism and follows it backwards (with appropriate flipped directions) to the g.M() location. */\n  followR(path: Path): Path;\n  /** The same as follow but follows the chain recursively. Starts as if at the g.M() and follows through the morphism path multiple times, returning all nodes encountered. */\n  followRecursive(path: Path): Path;\n  /** Call callback(data) for each result, where data is the tag-to-string map as in All case.\n   * @param [limit] An integer value on the first `limit` paths to process.\n   * @param callback: A javascript function of the form `function(data)`\n   */\n  forEach(callback: (data: { [key: string]: any }) => void): void;\n  forEach(\n    limit: number,\n    callback: (data: { [key: string]: any }) => void\n  ): void;\n  /** Alias for forEach. */\n  map(callback: (data: { [key: string]: any }) => void): void;\n  map(limit: number, callback: (data: { [key: string]: any }) => void): void;\n  /** The same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals. */\n  getLimit(limit: number): void;\n  /** Filter all paths which are, at this point, on the subject for the given predicate and object,\nbut do not follow the path, merely filter the possible paths. Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair.\n  * @param predicate A string for a predicate node.\n  * @param object A string for a object node or a set of filters to find it.\n  */\n  has(predicate: string, object: string): Path;\n  /** The same as Has, but sets constraint in reverse direction. */\n  hasR(predicate: string, object: string): Path;\n  /** The inverse of out. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects.\n   * @param [predicatePath] One of:\n   * * null or undefined: All predicates pointing into this node\n   * * a string: The predicate name to follow into this node\n   * * a list of strings: The predicates to follow into this node\n   * * a query path object: The target of which is a set of predicates to follow.\n   * @param [tags] One of:\n   * * null or undefined: No tags\n   * * a string: A single tag to add the predicate used to the output set.\n   * * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n   */\n  in(predicatePath?: Path, ...tags: string[]): Path;\n  /** Get the list of predicates that are pointing in to a node. */\n  inPredicates(): Path;\n  /** Filter all paths by the result of another query path. This is essentially a join where, at the stage of each path, a node is shared. */\n  intersect(path: Path): Path;\n  /** Filter all paths to ones which, at this point, are on the given node.\n   * @param node: A string for a node. Can be repeated or a list of strings.\n   */\n  is(node: string, ...nodes: string[]): Path;\n  /** Set (or remove) the subgraph context to consider in the following traversals.\n   * Affects all in(), out(), and both() calls that follow it. The default LabelContext is null (all subgraphs).\n   * @param predicatePath One of:\n   * * null or undefined: In future traversals, consider all edges, regardless of subgraph.\n   * * a string: The name of the subgraph to restrict traversals to.\n   * * a list of strings: A set of subgraphs to restrict traversals to.\n   * * a query path object: The target of which is a set of subgraphs.\n   * @param tags One of:\n   * * null or undefined: No tags\n   * * a string: A single tag to add the last traversed label to the output set.\n   * * a list of strings: Multiple tags to use as keys to save the label used to the output set.\n   */\n  labelContext(labelPath: Path, ...tags: string[]): Path;\n  /** Get the list of inbound and outbound quad labels */\n  labels(): Path;\n  /** Limit a number of nodes for current path. */\n  limit(limit: number): Path;\n  /** Alias for Union. */\n  or(path: Path): Path;\n  /** The work-a-day way to get between nodes, in the forward direction. Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects.\n   * @param predicatePath (Optional): One of:\n   * * null or undefined: All predicates pointing out from this node\n   * * a string: The predicate name to follow out from this node\n   * * a list of strings: The predicates to follow out from this node\n   * * a query path object: The target of which is a set of predicates to follow.\n   * @param tags (Optional): One of:\n   * * null or undefined: No tags\n   * * a string: A single tag to add the predicate used to the output set.\n   * * a list of strings: Multiple tags to use as keys to save the predicate used to the output set.\n   */\n  out(predicatePath?: Path, ...tags: string[]): Path;\n  /** Get the list of predicates that are pointing out from a node. */\n  outPredicates(): Path;\n  /** Save the object of all quads with predicate into tag, without traversal.\n   * @param predicate A string for a predicate node.\n   * @param tag A string for a tag key to store the object node.\n   */\n  save(predicate: string, tag: string): Path;\n  /** The same as save, but returns empty tags if predicate does not exists. */\n  saveOpt(predicate: string, tag: string): Path;\n  /** The same as saveOpt, but tags values via reverse predicate. */\n  saveOptR(predicate: string, tag: string): Path;\n  /** The same as save, but tags values via reverse predicate. */\n  saveR(predicate: string, tag: string): Path;\n  /** Tag the list of predicates that are pointing in to a node. */\n  saveInPredicates(tag: string): Path;\n  /** Tag the list of predicates that are pointing out from a node. */\n  saveOutPredicates(tag: string): Path;\n  /** Skip a number of nodes for current path.\n   * @param offset: A number of nodes to skip.\n   */\n  skip(offset: number): Path;\n  /** Save a list of nodes to a given tag. In order to save your work or learn more about how a path got to the end, we have tags.\nThe simplest thing to do is to add a tag anywhere you'd like to put each node in the result set.\n   * @param tag: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached \"Tag\" */\n  tag(...tags: string[]): Path;\n  /**\n   * The same as toArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment.\n   */\n  tagArray(): void;\n  /** The same as TagArray, but limited to one result node. Returns a tag-to-string map. */\n  tagValue(): void;\n  /** Execute a query and returns the results at the end of the query path as an JS array. */\n  toArray(): void;\n  /** The same as ToArray, but limited to one result node. */\n  toValue(): void;\n  /** Return the combined paths of the two queries. Notice that it's per-path, not per-node. Once again, if multiple paths reach the same destination, they might have had different ways of getting there (and different tags). See also: `Path.prototype.tag()` */\n  union(path: Path): Path;\n  /** Remove duplicate values from the path. */\n  unique(): Path;\n}\n\ninterface Graph {\n  /** A shorthand for Vertex. */\n  V(...nodeId: string[]): Path;\n  /** A shorthand for Morphism */\n  M(): Path;\n  /** Start a query path at the given vertex/vertices. No ids means \"all vertices\". */\n  Vertex(...nodeId: string[]): Path;\n  /** Create a morphism path object. Unqueryable on it's own, defines one end of the path.\nSaving these to variables with */\n  Morphism(): Path;\n  /** Load all namespaces saved to graph. */\n  loadNamespaces(): void;\n  /** Register all default namespaces for automatic IRI resolution. */\n  addDefaultNamespaces(): void;\n  /** Associate prefix with a given IRI namespace. */\n  addNamespace(): void;\n  /** Add data programmatically to the JSON result list. Can be any JSON type. */\n  emit(): void;\n  /** Create an IRI values from a given string. */\n  IRI(): string;\n}\n\n/** This is the only special object in the environment, generates the query objects.\nUnder the hood, they're simple objects that get compiled to a Go iterator tree when executed. */\ndeclare var graph: Graph;\n\n/** Alias of graph. This is the only special object in the environment, generates the query objects.\nUnder the hood, they're simple objects that get compiled to a Go iterator tree when executed. */\ndeclare var g: Graph;\n\ninterface RegexFilter {}\n\n/** Filter by match a regular expression ([syntax](https://github.com/google/re2/wiki/Syntax)). By default works only on literals unless includeIRIs is set to `true`. */\ndeclare function regex(expression: string, includeIRIs?: boolean): RegexFilter;\n"
  },
  {
    "path": "ui/web/index.html",
    "content": "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"/><link rel=\"shortcut icon\" href=\"/favicon.ico\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/><meta name=\"theme-color\" content=\"#000000\"/><meta name=\"description\" content=\"The Open Source Knowledge Graph\"/><link rel=\"apple-touch-icon\" href=\"logo192.png\"/><link rel=\"manifest\" href=\"/manifest.json\"/><title>Cayley</title><link href=\"/static/css/2.6b51f286.chunk.css\" rel=\"stylesheet\"><link href=\"/static/css/main.72fe53e6.chunk.css\" rel=\"stylesheet\"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id=\"root\"></div><script>!function(a){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],l=0,i=[];l<n.length;l++)t=n[l],Object.prototype.hasOwnProperty.call(c,t)&&c[t]&&i.push(c[t][0]),c[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(a[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return p.push.apply(p,u||[]),f()}function f(){for(var e,r=0;r<p.length;r++){for(var t=p[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==c[u]&&(n=!1)}n&&(p.splice(r--,1),e=l(l.s=t[0]))}return e}var t={},c={1:0},p=[];function l(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return a[e].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=a,l.c=t,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},l.t=function(r,e){if(1&e&&(r=l(r)),8&e)return r;if(4&e&&\"object\"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,\"default\",{enumerable:!0,value:r}),2&e&&\"string\"!=typeof r)for(var n in r)l.d(t,n,function(e){return r[e]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,\"a\",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p=\"/\";var r=this[\"webpackJsonpcayley-ui\"]=this[\"webpackJsonpcayley-ui\"]||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;f()}([])</script><script src=\"/static/js/2.7d84b3fa.chunk.js\"></script><script src=\"/static/js/main.84d3ab8c.chunk.js\"></script></body></html>"
  },
  {
    "path": "ui/web/manifest.json",
    "content": "{\n  \"short_name\": \"Cayley\",\n  \"name\": \"Cayley\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    },\n    {\n      \"src\": \"logo192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\"\n    },\n    {\n      \"src\": \"logo512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "ui/web/precache-manifest.5316e0e4a35813e95b82d4799f1bb55c.js",
    "content": "self.__precacheManifest = (self.__precacheManifest || []).concat([\n  {\n    \"revision\": \"04b3493090e1efb6ffc5ded2f5343ae0\",\n    \"url\": \"/index.html\"\n  },\n  {\n    \"revision\": \"8fcf0599cb1e0923f5dd\",\n    \"url\": \"/static/css/2.6b51f286.chunk.css\"\n  },\n  {\n    \"revision\": \"c254b9a695b5dbe62b1f\",\n    \"url\": \"/static/css/main.72fe53e6.chunk.css\"\n  },\n  {\n    \"revision\": \"8fcf0599cb1e0923f5dd\",\n    \"url\": \"/static/js/2.7d84b3fa.chunk.js\"\n  },\n  {\n    \"revision\": \"c254b9a695b5dbe62b1f\",\n    \"url\": \"/static/js/main.84d3ab8c.chunk.js\"\n  },\n  {\n    \"revision\": \"d18b22d979014e3125a8\",\n    \"url\": \"/static/js/runtime-main.0686c6e7.js\"\n  },\n  {\n    \"revision\": \"ca71146870e23a16db96ef358169e8fa\",\n    \"url\": \"/static/media/logo.ca711468.svg\"\n  }\n]);"
  },
  {
    "path": "ui/web/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\n"
  },
  {
    "path": "ui/web/service-worker.js",
    "content": "/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app and you should\n * disable HTTP caching for this file too.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\nimportScripts(\"https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js\");\n\nimportScripts(\n  \"/precache-manifest.5316e0e4a35813e95b82d4799f1bb55c.js\"\n);\n\nself.addEventListener('message', (event) => {\n  if (event.data && event.data.type === 'SKIP_WAITING') {\n    self.skipWaiting();\n  }\n});\n\nworkbox.core.clientsClaim();\n\n/**\n * The workboxSW.precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nself.__precacheManifest = [].concat(self.__precacheManifest || []);\nworkbox.precaching.precacheAndRoute(self.__precacheManifest, {});\n\nworkbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL(\"/index.html\"), {\n  \n  blacklist: [/^\\/_/,/\\/[^/?]+\\.[^/]+$/],\n});\n"
  },
  {
    "path": "ui/web/static/css/2.6b51f286.chunk.css",
    "content": "/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-card{border-radius:4px;background-color:#fff;background-color:var(--mdc-theme-surface,#fff);box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12);display:flex;flex-direction:column;box-sizing:border-box}.mdc-card--outlined{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12);border:1px solid #e0e0e0}.mdc-card__media{position:relative;box-sizing:border-box;background-repeat:no-repeat;background-position:50%;background-size:cover}.mdc-card__media:before{display:block;content:\"\"}.mdc-card__media:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.mdc-card__media:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.mdc-card__media--square:before{margin-top:100%}.mdc-card__media--16-9:before{margin-top:56.25%}.mdc-card__media-content{position:absolute;top:0;right:0;bottom:0;left:0;box-sizing:border-box}.mdc-card__primary-action{display:flex;flex-direction:column;box-sizing:border-box;position:relative;outline:none;color:inherit;text-decoration:none;cursor:pointer;overflow:hidden}.mdc-card__primary-action:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.mdc-card__primary-action:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.mdc-card__actions{display:flex;flex-direction:row;align-items:center;box-sizing:border-box;min-height:52px;padding:8px}.mdc-card__actions--full-bleed{padding:0}.mdc-card__action-buttons,.mdc-card__action-icons{display:flex;flex-direction:row;align-items:center;box-sizing:border-box}.mdc-card__action-icons{color:rgba(0,0,0,.6);flex-grow:1;justify-content:flex-end}.mdc-card__action-buttons+.mdc-card__action-icons{margin-left:16px;margin-right:0}.mdc-card__action-buttons+.mdc-card__action-icons[dir=rtl],[dir=rtl] .mdc-card__action-buttons+.mdc-card__action-icons{margin-left:0;margin-right:16px}.mdc-card__action{display:inline-flex;flex-direction:row;align-items:center;box-sizing:border-box;justify-content:center;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdc-card__action:focus{outline:none}.mdc-card__action--button{margin-left:0;margin-right:8px;padding:0 8px}.mdc-card__action--button[dir=rtl],[dir=rtl] .mdc-card__action--button{margin-left:8px;margin-right:0}.mdc-card__action--button:last-child,.mdc-card__action--button:last-child[dir=rtl],[dir=rtl] .mdc-card__action--button:last-child{margin-left:0;margin-right:0}.mdc-card__actions--full-bleed .mdc-card__action--button{justify-content:space-between;width:100%;height:auto;max-height:none;margin:0;padding:8px 16px;text-align:left}.mdc-card__actions--full-bleed .mdc-card__action--button[dir=rtl],[dir=rtl] .mdc-card__actions--full-bleed .mdc-card__action--button{text-align:right}.mdc-card__action--icon{margin:-6px 0;padding:12px}.mdc-card__action--icon:not(:disabled){color:rgba(0,0,0,.6)}.mdc-card__primary-action{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-card__primary-action:after,.mdc-card__primary-action:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:\"\"}.mdc-card__primary-action:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}.mdc-card__primary-action.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}.mdc-card__primary-action.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-card__primary-action.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}.mdc-card__primary-action.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-card__primary-action.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}.mdc-card__primary-action:after,.mdc-card__primary-action:before{top:-50%;left:-50%;width:200%;height:200%}.mdc-card__primary-action.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}.mdc-card__primary-action:after,.mdc-card__primary-action:before{background-color:#000}.mdc-card__primary-action:hover:before{opacity:.04}.mdc-card__primary-action.mdc-ripple-upgraded--background-focused:before,.mdc-card__primary-action:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-card__primary-action:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-card__primary-action:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-card__primary-action.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-tab-scroller{overflow-y:hidden}.mdc-tab-scroller__test{position:absolute;top:-9999px;width:100px;height:100px;overflow-x:scroll}.mdc-tab-scroller__scroll-area{-webkit-overflow-scrolling:touch;display:flex;overflow-x:hidden}.mdc-tab-scroller__scroll-area::-webkit-scrollbar,.mdc-tab-scroller__test::-webkit-scrollbar{display:none}.mdc-tab-scroller__scroll-area--scroll{overflow-x:scroll}.mdc-tab-scroller__scroll-content{position:relative;display:flex;flex:1 0 auto;-webkit-transform:none;transform:none;will-change:transform}.mdc-tab-scroller--align-start .mdc-tab-scroller__scroll-content{justify-content:flex-start}.mdc-tab-scroller--align-end .mdc-tab-scroller__scroll-content{justify-content:flex-end}.mdc-tab-scroller--align-center .mdc-tab-scroller__scroll-content{justify-content:center}.mdc-tab-scroller--animating .mdc-tab-scroller__scroll-area{-webkit-overflow-scrolling:auto}.mdc-tab-scroller--animating .mdc-tab-scroller__scroll-content{transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1);-webkit-transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),-webkit-transform .25s cubic-bezier(.4,0,.2,1)}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-tab-indicator{display:flex;position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}.mdc-tab-indicator>.mdc-tab-indicator__content--underline{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee);height:2px}.mdc-tab-indicator>.mdc-tab-indicator__content--icon{color:#018786;color:var(--mdc-theme-secondary,#018786);height:34px;font-size:34px}.mdc-tab-indicator__content{-webkit-transform-origin:left;transform-origin:left;opacity:0}.mdc-tab-indicator__content--underline{align-self:flex-end;width:100%}.mdc-tab-indicator__content--icon{align-self:center;margin:0 auto}.mdc-tab-indicator--active>.mdc-tab-indicator__content{opacity:1}.mdc-tab-indicator>.mdc-tab-indicator__content{transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1);-webkit-transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),-webkit-transform .25s cubic-bezier(.4,0,.2,1)}.mdc-tab-indicator--no-transition>.mdc-tab-indicator__content{-webkit-transition:none;transition:none}.mdc-tab-indicator--fade>.mdc-tab-indicator__content{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-tab-indicator--active.mdc-tab-indicator--fade>.mdc-tab-indicator__content{-webkit-transition-delay:.1s;transition-delay:.1s}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-tab-bar{width:100%}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-tab{position:relative;font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:2.25rem;font-weight:500;letter-spacing:.0892857143em;text-decoration:none;text-transform:uppercase;display:flex;flex:1 0 auto;justify-content:center;box-sizing:border-box;height:48px;padding:0 24px;border:none;outline:none;background:none;text-align:center;white-space:nowrap;cursor:pointer;-webkit-appearance:none;z-index:1}.mdc-tab .mdc-tab__icon,.mdc-tab .mdc-tab__text-label{color:#000;color:var(--mdc-theme-on-surface,#000)}.mdc-tab .mdc-tab__icon{fill:currentColor}.mdc-tab::-moz-focus-inner{padding:0;border:0}.mdc-tab--min-width{flex:0 1 auto}.mdc-tab__ripple{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity;position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden}.mdc-tab__ripple:after,.mdc-tab__ripple:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:\"\"}.mdc-tab__ripple:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}.mdc-tab__ripple.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}.mdc-tab__ripple.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-tab__ripple.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}.mdc-tab__ripple.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-tab__ripple.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}.mdc-tab__ripple:after,.mdc-tab__ripple:before{top:-50%;left:-50%;width:200%;height:200%}.mdc-tab__ripple.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}.mdc-tab__ripple:after,.mdc-tab__ripple:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){.mdc-tab__ripple:after,.mdc-tab__ripple:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}.mdc-tab__ripple:hover:before{opacity:.04}.mdc-tab__ripple.mdc-ripple-upgraded--background-focused:before,.mdc-tab__ripple:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-tab__ripple:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-tab__ripple:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-tab__ripple.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}.mdc-tab__content{position:relative;display:flex;align-items:center;justify-content:center;height:inherit;pointer-events:none}.mdc-tab__icon,.mdc-tab__text-label{-webkit-transition:color .15s linear,opacity .15s linear;transition:color .15s linear,opacity .15s linear;z-index:2}.mdc-tab__text-label{display:inline-block;opacity:.6;line-height:1}.mdc-tab__icon{width:24px;height:24px;opacity:.54;font-size:24px}.mdc-tab--stacked{height:72px}.mdc-tab--stacked .mdc-tab__content{flex-direction:column;align-items:center;justify-content:space-between}.mdc-tab--stacked .mdc-tab__icon{padding-top:12px}.mdc-tab--stacked .mdc-tab__text-label{padding-bottom:16px}.mdc-tab--active .mdc-tab__icon,.mdc-tab--active .mdc-tab__text-label{color:#6200ee;color:var(--mdc-theme-primary,#6200ee)}.mdc-tab--active .mdc-tab__icon{fill:currentColor}.mdc-tab--active .mdc-tab__icon,.mdc-tab--active .mdc-tab__text-label{-webkit-transition-delay:.1s;transition-delay:.1s;opacity:1}.mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label{padding-left:8px;padding-right:0}.mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label[dir=rtl],[dir=rtl] .mdc-tab:not(.mdc-tab--stacked) .mdc-tab__icon+.mdc-tab__text-label{padding-left:0;padding-right:8px}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-typography{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.mdc-typography--headline1{font-size:6rem;line-height:6rem;letter-spacing:-.015625em}.mdc-typography--headline1,.mdc-typography--headline2{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:300;text-decoration:inherit;text-transform:inherit}.mdc-typography--headline2{font-size:3.75rem;line-height:3.75rem;letter-spacing:-.0083333333em}.mdc-typography--headline3{font-size:3rem;line-height:3.125rem;letter-spacing:normal}.mdc-typography--headline3,.mdc-typography--headline4{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:400;text-decoration:inherit;text-transform:inherit}.mdc-typography--headline4{font-size:2.125rem;line-height:2.5rem;letter-spacing:.0073529412em}.mdc-typography--headline5{font-size:1.5rem;font-weight:400;letter-spacing:normal}.mdc-typography--headline5,.mdc-typography--headline6{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;line-height:2rem;text-decoration:inherit;text-transform:inherit}.mdc-typography--headline6{font-size:1.25rem;font-weight:500;letter-spacing:.0125em}.mdc-typography--subtitle1{font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em}.mdc-typography--subtitle1,.mdc-typography--subtitle2{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-decoration:inherit;text-transform:inherit}.mdc-typography--subtitle2{font-size:.875rem;line-height:1.375rem;font-weight:500;letter-spacing:.0071428571em}.mdc-typography--body1{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.5rem;font-weight:400;letter-spacing:.03125em;text-decoration:inherit;text-transform:inherit}.mdc-typography--body2{font-size:.875rem;letter-spacing:.0178571429em}.mdc-typography--body2,.mdc-typography--caption{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;line-height:1.25rem;font-weight:400;text-decoration:inherit;text-transform:inherit}.mdc-typography--caption{font-size:.75rem;letter-spacing:.0333333333em}.mdc-typography--button{font-size:.875rem;line-height:2.25rem;letter-spacing:.0892857143em}.mdc-typography--button,.mdc-typography--overline{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:500;text-decoration:none;text-transform:uppercase}.mdc-typography--overline{font-size:.75rem;line-height:2rem;letter-spacing:.1666666667em}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/@-webkit-keyframes mdc-select-float-native-control{0%{-webkit-transform:translateY(8px);transform:translateY(8px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes mdc-select-float-native-control{0%{-webkit-transform:translateY(8px);transform:translateY(8px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.mdc-line-ripple{position:absolute;bottom:0;left:0;width:100%;height:2px;-webkit-transform:scaleX(0);transform:scaleX(0);transition:opacity .18s cubic-bezier(.4,0,.2,1),-webkit-transform .18s cubic-bezier(.4,0,.2,1);-webkit-transition:opacity .18s cubic-bezier(.4,0,.2,1),-webkit-transform .18s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),opacity .18s cubic-bezier(.4,0,.2,1);transition:transform .18s cubic-bezier(.4,0,.2,1),opacity .18s cubic-bezier(.4,0,.2,1),-webkit-transform .18s cubic-bezier(.4,0,.2,1);opacity:0;z-index:2}.mdc-line-ripple--active{-webkit-transform:scaleX(1);transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating{opacity:0}.mdc-notched-outline{display:flex;position:absolute;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}.mdc-notched-outline[dir=rtl],[dir=rtl] .mdc-notched-outline{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;-webkit-transition:border .15s cubic-bezier(.4,0,.2,1);transition:border .15s cubic-bezier(.4,0,.2,1);border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}.mdc-notched-outline__leading[dir=rtl],.mdc-notched-outline__trailing,[dir=rtl] .mdc-notched-outline__leading{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{flex-grow:1}.mdc-notched-outline__trailing[dir=rtl],[dir=rtl] .mdc-notched-outline__trailing{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 24px)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;top:17px;bottom:auto;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:133.33333%}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl],[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{padding:0}.mdc-floating-label{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;transition:color .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1);-webkit-transition:color .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1),color .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1);line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform}.mdc-floating-label[dir=rtl],[dir=rtl] .mdc-floating-label{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto;-webkit-transform:translateY(-50%) scale(.75);transform:translateY(-50%) scale(.75)}.mdc-floating-label--shake{-webkit-animation:mdc-floating-label-shake-float-above-standard .25s 1;animation:mdc-floating-label-shake-float-above-standard .25s 1}@-webkit-keyframes mdc-floating-label-shake-float-above-standard{0%{-webkit-transform:translateX(0) translateY(-50%) scale(.75);transform:translateX(0) translateY(-50%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(4%) translateY(-50%) scale(.75);transform:translateX(4%) translateY(-50%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(-4%) translateY(-50%) scale(.75);transform:translateX(-4%) translateY(-50%) scale(.75)}to{-webkit-transform:translateX(0) translateY(-50%) scale(.75);transform:translateX(0) translateY(-50%) scale(.75)}}@keyframes mdc-floating-label-shake-float-above-standard{0%{-webkit-transform:translateX(0) translateY(-50%) scale(.75);transform:translateX(0) translateY(-50%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(4%) translateY(-50%) scale(.75);transform:translateX(4%) translateY(-50%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(-4%) translateY(-50%) scale(.75);transform:translateX(-4%) translateY(-50%) scale(.75)}to{-webkit-transform:translateX(0) translateY(-50%) scale(.75);transform:translateX(0) translateY(-50%) scale(.75)}}.mdc-select--with-leading-icon:not(.mdc-select--disabled) .mdc-select__icon{color:#000;color:var(--mdc-theme-on-surface,#000)}.mdc-select--with-leading-icon .mdc-select__icon{display:inline-block;position:absolute;bottom:16px;box-sizing:border-box;width:24px;height:24px;border:none;background-color:transparent;fill:currentColor;opacity:.54;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mdc-select__icon:not([tabindex]),.mdc-select__icon[tabindex=\"-1\"]{cursor:default;pointer-events:none}.mdc-select-helper-text{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.75rem;line-height:1.25rem;font-weight:400;letter-spacing:.0333333333em;text-decoration:inherit;text-transform:inherit;display:block;line-height:normal;margin:0;-webkit-transition:opacity .18s cubic-bezier(.4,0,.2,1);transition:opacity .18s cubic-bezier(.4,0,.2,1);opacity:0;will-change:opacity}.mdc-select-helper-text:before{display:inline-block;width:0;height:16px;content:\"\";vertical-align:0}.mdc-select-helper-text--persistent{-webkit-transition:none;transition:none;opacity:1;will-change:auto}.mdc-select{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity;display:inline-flex;position:relative;box-sizing:border-box;height:56px;overflow:hidden;will-change:opacity,transform,color}.mdc-select:not(.mdc-select--disabled){background-color:#f5f5f5}.mdc-select:after,.mdc-select:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:\"\"}.mdc-select:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}.mdc-select.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}.mdc-select.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-select.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}.mdc-select.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-select.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}.mdc-select:after,.mdc-select:before{top:-50%;left:-50%;width:200%;height:200%}.mdc-select.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}.mdc-select:after,.mdc-select:before{background-color:rgba(0,0,0,.87)}.mdc-select:hover:before{opacity:.04}.mdc-select.mdc-ripple-upgraded--background-focused:before,.mdc-select:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-select:not(.mdc-select--disabled) .mdc-select__native-control,.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0,0,0,.87)}.mdc-select:not(.mdc-select--disabled) .mdc-floating-label{color:rgba(0,0,0,.6)}.mdc-select:not(.mdc-select--disabled) .mdc-select__native-control,.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{border-bottom-color:rgba(0,0,0,.42)}.mdc-select:not(.mdc-select--disabled)+.mdc-select-helper-text{color:rgba(0,0,0,.6)}.mdc-select,.mdc-select__native-control{border-radius:4px 4px 0 0}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-line-ripple{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:rgba(98,0,238,.87)}.mdc-select:not(.mdc-select--disabled) .mdc-select__native-control:hover{border-bottom-color:rgba(0,0,0,.87)}.mdc-select .mdc-floating-label--float-above{-webkit-transform:translateY(-70%) scale(.75);transform:translateY(-70%) scale(.75)}.mdc-select .mdc-floating-label{left:16px;right:auto;top:21px;pointer-events:none}.mdc-select .mdc-floating-label[dir=rtl],[dir=rtl] .mdc-select .mdc-floating-label{left:auto;right:16px}.mdc-select.mdc-select--with-leading-icon .mdc-floating-label{left:48px;right:auto}.mdc-select.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl],[dir=rtl] .mdc-select.mdc-select--with-leading-icon .mdc-floating-label{left:auto;right:48px}.mdc-select.mdc-select--outlined .mdc-floating-label{left:4px;right:auto;top:17px}.mdc-select.mdc-select--outlined .mdc-floating-label[dir=rtl],[dir=rtl] .mdc-select.mdc-select--outlined .mdc-floating-label{left:auto;right:4px}.mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:36px;right:auto}.mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl],[dir=rtl] .mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:auto;right:36px}.mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{left:36px;right:auto}.mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above[dir=rtl],[dir=rtl] .mdc-select.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{left:auto;right:36px}.mdc-select__dropdown-icon{background:url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='5' viewBox='7 10 10 5' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' opacity='.54' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\") no-repeat 50%;left:auto;right:8px;position:absolute;bottom:16px;width:24px;height:24px;transition:-webkit-transform .15s cubic-bezier(.4,0,.2,1);-webkit-transition:-webkit-transform .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1);pointer-events:none}.mdc-select__dropdown-icon[dir=rtl],[dir=rtl] .mdc-select__dropdown-icon{left:8px;right:auto}.mdc-select--focused .mdc-select__dropdown-icon{background:url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='5' viewBox='7 10 10 5' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%236200ee' fill-rule='evenodd' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\") no-repeat 50%;-webkit-transform:rotate(180deg) translateY(-5px);transform:rotate(180deg) translateY(-5px);transition:-webkit-transform .15s cubic-bezier(.4,0,.2,1);-webkit-transition:-webkit-transform .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1);transition:transform .15s cubic-bezier(.4,0,.2,1),-webkit-transform .15s cubic-bezier(.4,0,.2,1)}.mdc-select__native-control{padding-top:20px}.mdc-select.mdc-select--focused .mdc-line-ripple:after{-webkit-transform:scaleY(2);transform:scaleY(2);opacity:1}.mdc-select+.mdc-select-helper-text{margin-right:12px;margin-left:12px}.mdc-select--outlined+.mdc-select-helper-text{margin-right:16px;margin-left:16px}.mdc-select--focused+.mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg){opacity:1}.mdc-select__selected-text{min-width:200px;padding-top:22px}.mdc-select__native-control,.mdc-select__selected-text{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;box-sizing:border-box;width:100%;height:56px;padding:20px 52px 4px 16px;border:none;border-bottom:1px solid;outline:none;background-color:transparent;color:inherit;white-space:nowrap;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.mdc-select__native-control[dir=rtl],.mdc-select__selected-text[dir=rtl],[dir=rtl] .mdc-select__native-control,[dir=rtl] .mdc-select__selected-text{padding-left:52px;padding-right:16px}.mdc-select__native-control::-ms-expand,.mdc-select__selected-text::-ms-expand{display:none}.mdc-select__native-control::-ms-value,.mdc-select__selected-text::-ms-value{background-color:transparent;color:inherit}@-moz-document url-prefix(\"\"){.mdc-select__native-control,.mdc-select__selected-text{text-indent:-2px}}.mdc-select--outlined{border:none;overflow:visible}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:rgba(0,0,0,.24)}.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0,0,0,.87)}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px;border-color:#6200ee;border-color:var(--mdc-theme-primary,#6200ee)}.mdc-select--outlined .mdc-floating-label--shake{-webkit-animation:mdc-floating-label-shake-float-above-text-field-outlined .25s 1;animation:mdc-floating-label-shake-float-above-text-field-outlined .25s 1}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-radius:4px 0 0 4px}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl],.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing,[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-radius:0 4px 4px 0}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl],[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-radius:4px 0 0 4px}.mdc-select--outlined .mdc-select__native-control{border-radius:4px}.mdc-select--outlined:after,.mdc-select--outlined:before{content:none}.mdc-select--outlined:not(.mdc-select--disabled){background-color:transparent}.mdc-select--outlined .mdc-floating-label--float-above{-webkit-transform:translateY(-144%) scale(1);transform:translateY(-144%) scale(1);font-size:.75rem}.mdc-select--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{-webkit-transform:translateY(-130%) scale(.75);transform:translateY(-130%) scale(.75);font-size:1rem}.mdc-select--outlined .mdc-select__native-control,.mdc-select--outlined .mdc-select__selected-text{display:flex;padding:12px 52px 12px 16px;border:none;background-color:transparent;z-index:1}.mdc-select--outlined .mdc-select__native-control[dir=rtl],.mdc-select--outlined .mdc-select__selected-text[dir=rtl],[dir=rtl] .mdc-select--outlined .mdc-select__native-control,[dir=rtl] .mdc-select--outlined .mdc-select__selected-text{padding-left:52px;padding-right:16px}.mdc-select--outlined .mdc-select__selected-text{padding-top:14px}.mdc-select--outlined .mdc-select__icon{z-index:2}.mdc-select--outlined .mdc-floating-label{line-height:1.15rem;pointer-events:auto}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__native-control,.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__selected-text{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-line-ripple{background-color:#b00020;background-color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:#b00020}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--invalid+.mdc-select-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__native-control:hover{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__native-control:hover~.mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__selected-text:hover~.mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--invalid.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px;border-color:#b00020;border-color:var(--mdc-theme-error,#b00020)}.mdc-select--invalid .mdc-select__dropdown-icon{background:url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='5' viewBox='7 10 10 5' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23b00020' fill-rule='evenodd' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\") no-repeat 50%}.mdc-select--invalid+.mdc-select-helper-text--validation-msg{opacity:1}.mdc-select--required .mdc-floating-label:after{content:\"*\"}.mdc-select--disabled{background-color:#fafafa;cursor:default;pointer-events:none}.mdc-select--disabled .mdc-floating-label{color:rgba(0,0,0,.37)}.mdc-select--disabled .mdc-select__dropdown-icon{background:url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='5' viewBox='7 10 10 5' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' opacity='.37' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E\") no-repeat 50%}.mdc-select--disabled .mdc-line-ripple{display:none}.mdc-select--disabled .mdc-select__icon{color:rgba(0,0,0,.37)}.mdc-select--disabled .mdc-select__native-control,.mdc-select--disabled .mdc-select__selected-text{color:rgba(0,0,0,.37);border-bottom-style:dotted}.mdc-select--disabled .mdc-select__selected-text{pointer-events:none}.mdc-select--disabled.mdc-select--outlined{background-color:transparent}.mdc-select--disabled.mdc-select--outlined .mdc-select__native-control,.mdc-select--disabled.mdc-select--outlined .mdc-select__selected-text{border-bottom-style:none}.mdc-select--disabled.mdc-select--outlined .mdc-notched-outline__leading,.mdc-select--disabled.mdc-select--outlined .mdc-notched-outline__notch,.mdc-select--disabled.mdc-select--outlined .mdc-notched-outline__trailing{border-color:rgba(0,0,0,.16)}.mdc-select--with-leading-icon .mdc-select__icon{left:16px;right:auto}.mdc-select--with-leading-icon .mdc-select__icon[dir=rtl],[dir=rtl] .mdc-select--with-leading-icon .mdc-select__icon{left:auto;right:16px}.mdc-select--with-leading-icon .mdc-select__native-control,.mdc-select--with-leading-icon .mdc-select__selected-text{padding-left:48px;padding-right:32px}.mdc-select--with-leading-icon .mdc-select__native-control[dir=rtl],.mdc-select--with-leading-icon .mdc-select__selected-text[dir=rtl],[dir=rtl] .mdc-select--with-leading-icon .mdc-select__native-control,[dir=rtl] .mdc-select--with-leading-icon .mdc-select__selected-text{padding-left:32px;padding-right:48px}.mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--float-above{-webkit-transform:translateY(-144%) translateX(-32px) scale(1);transform:translateY(-144%) translateX(-32px) scale(1)}.mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--float-above[dir=rtl],[dir=rtl] .mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--float-above{-webkit-transform:translateY(-144%) translateX(32px) scale(1);transform:translateY(-144%) translateX(32px) scale(1)}.mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--with-leading-icon.mdc-select--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--with-leading-icon.mdc-select--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{-webkit-transform:translateY(-130%) translateX(-32px) scale(.75);transform:translateY(-130%) translateX(-32px) scale(.75)}.mdc-select--with-leading-icon.mdc-select--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-select--with-leading-icon.mdc-select--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],[dir=rtl] .mdc-select--with-leading-icon.mdc-select--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-select--with-leading-icon.mdc-select--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{-webkit-transform:translateY(-130%) translateX(32px) scale(.75);transform:translateY(-130%) translateX(32px) scale(.75)}.mdc-select--with-leading-icon.mdc-select--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--with-leading-icon.mdc-select--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--shake{-webkit-animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon .25s 1;animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon .25s 1}.mdc-select--with-leading-icon.mdc-select--outlined[dir=rtl] .mdc-floating-label--shake,[dir=rtl] .mdc-select--with-leading-icon.mdc-select--outlined .mdc-floating-label--shake{-webkit-animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-rtl .25s 1;animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-rtl .25s 1}.mdc-select--with-leading-icon.mdc-select__menu .mdc-list-item__text,.mdc-select--with-leading-icon.mdc-select__menu .mdc-list-item__text[dir=rtl],[dir=rtl] .mdc-select--with-leading-icon.mdc-select__menu .mdc-list-item__text{padding-left:32px;padding-right:32px}.mdc-select__menu .mdc-list .mdc-list-item--selected{color:#000;color:var(--mdc-theme-on-surface,#000)}.mdc-select__menu .mdc-list .mdc-list-item--selected:after,.mdc-select__menu .mdc-list .mdc-list-item--selected:before{background-color:#000}@supports not (-ms-ime-align:auto){.mdc-select__menu .mdc-list .mdc-list-item--selected:after,.mdc-select__menu .mdc-list .mdc-list-item--selected:before{background-color:#000;background-color:var(--mdc-theme-on-surface,#000)}}.mdc-select__menu .mdc-list .mdc-list-item--selected:hover:before{opacity:.04}.mdc-select__menu .mdc-list .mdc-list-item--selected.mdc-ripple-upgraded--background-focused:before,.mdc-select__menu .mdc-list .mdc-list-item--selected:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-select__menu .mdc-list .mdc-list-item--selected:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-select__menu .mdc-list .mdc-list-item--selected:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-select__menu .mdc-list .mdc-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}@-webkit-keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon{0%{-webkit-transform:translateX(-32px) translateY(-130%) scale(.75);transform:translateX(-32px) translateY(-130%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(calc(4% - 32px)) translateY(-130%) scale(.75);transform:translateX(calc(4% - 32px)) translateY(-130%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(calc(-4% - 32px)) translateY(-130%) scale(.75);transform:translateX(calc(-4% - 32px)) translateY(-130%) scale(.75)}to{-webkit-transform:translateX(-32px) translateY(-130%) scale(.75);transform:translateX(-32px) translateY(-130%) scale(.75)}}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon{0%{-webkit-transform:translateX(-32px) translateY(-130%) scale(.75);transform:translateX(-32px) translateY(-130%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(calc(4% - 32px)) translateY(-130%) scale(.75);transform:translateX(calc(4% - 32px)) translateY(-130%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(calc(-4% - 32px)) translateY(-130%) scale(.75);transform:translateX(calc(-4% - 32px)) translateY(-130%) scale(.75)}to{-webkit-transform:translateX(-32px) translateY(-130%) scale(.75);transform:translateX(-32px) translateY(-130%) scale(.75)}}@-webkit-keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-rtl{0%{-webkit-transform:translateX(32px) translateY(-130%) scale(.75);transform:translateX(32px) translateY(-130%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(calc(4% - -32px)) translateY(-130%) scale(.75);transform:translateX(calc(4% - -32px)) translateY(-130%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(calc(-4% - -32px)) translateY(-130%) scale(.75);transform:translateX(calc(-4% - -32px)) translateY(-130%) scale(.75)}to{-webkit-transform:translateX(32px) translateY(-130%) scale(.75);transform:translateX(32px) translateY(-130%) scale(.75)}}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-rtl{0%{-webkit-transform:translateX(32px) translateY(-130%) scale(.75);transform:translateX(32px) translateY(-130%) scale(.75)}33%{-webkit-animation-timing-function:cubic-bezier(.5,0,.701732,.495819);animation-timing-function:cubic-bezier(.5,0,.701732,.495819);-webkit-transform:translateX(calc(4% - -32px)) translateY(-130%) scale(.75);transform:translateX(calc(4% - -32px)) translateY(-130%) scale(.75)}66%{-webkit-animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);-webkit-transform:translateX(calc(-4% - -32px)) translateY(-130%) scale(.75);transform:translateX(calc(-4% - -32px)) translateY(-130%) scale(.75)}to{-webkit-transform:translateX(32px) translateY(-130%) scale(.75);transform:translateX(32px) translateY(-130%) scale(.75)}}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-button{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:2.25rem;font-weight:500;letter-spacing:.0892857143em;text-decoration:none;text-transform:uppercase;padding:0 8px;display:inline-flex;position:relative;align-items:center;justify-content:center;box-sizing:border-box;min-width:64px;height:36px;border:none;outline:none;line-height:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-appearance:none;overflow:hidden;vertical-align:middle;border-radius:4px}.mdc-button::-moz-focus-inner{padding:0;border:0}.mdc-button:active{outline:none}.mdc-button:hover{cursor:pointer}.mdc-button:disabled{background-color:transparent;color:rgba(0,0,0,.37);cursor:default;pointer-events:none}.mdc-button.mdc-button--dense{border-radius:4px}.mdc-button:not(:disabled){background-color:transparent}.mdc-button .mdc-button__icon{margin-left:0;margin-right:8px;display:inline-block;width:18px;height:18px;font-size:18px;vertical-align:top}.mdc-button .mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button .mdc-button__icon{margin-left:8px;margin-right:0}.mdc-button:not(:disabled){color:#6200ee;color:var(--mdc-theme-primary,#6200ee)}.mdc-button__label+.mdc-button__icon{margin-left:8px;margin-right:0}.mdc-button__label+.mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button__label+.mdc-button__icon{margin-left:0;margin-right:8px}svg.mdc-button__icon{fill:currentColor}.mdc-button--outlined .mdc-button__icon,.mdc-button--raised .mdc-button__icon,.mdc-button--unelevated .mdc-button__icon{margin-left:-4px;margin-right:8px}.mdc-button--outlined .mdc-button__icon[dir=rtl],.mdc-button--outlined .mdc-button__label+.mdc-button__icon,.mdc-button--raised .mdc-button__icon[dir=rtl],.mdc-button--raised .mdc-button__label+.mdc-button__icon,.mdc-button--unelevated .mdc-button__icon[dir=rtl],.mdc-button--unelevated .mdc-button__label+.mdc-button__icon,[dir=rtl] .mdc-button--outlined .mdc-button__icon,[dir=rtl] .mdc-button--raised .mdc-button__icon,[dir=rtl] .mdc-button--unelevated .mdc-button__icon{margin-left:8px;margin-right:-4px}.mdc-button--outlined .mdc-button__label+.mdc-button__icon[dir=rtl],.mdc-button--raised .mdc-button__label+.mdc-button__icon[dir=rtl],.mdc-button--unelevated .mdc-button__label+.mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--outlined .mdc-button__label+.mdc-button__icon,[dir=rtl] .mdc-button--raised .mdc-button__label+.mdc-button__icon,[dir=rtl] .mdc-button--unelevated .mdc-button__label+.mdc-button__icon{margin-left:-4px;margin-right:8px}.mdc-button--raised,.mdc-button--unelevated{padding:0 16px}.mdc-button--raised:disabled,.mdc-button--unelevated:disabled{background-color:rgba(0,0,0,.12);color:rgba(0,0,0,.37)}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){background-color:#6200ee}@supports not (-ms-ime-align:auto){.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){color:#fff;color:var(--mdc-theme-on-primary,#fff)}.mdc-button--raised{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);-webkit-transition:box-shadow .28s cubic-bezier(.4,0,.2,1);transition:box-shadow .28s cubic-bezier(.4,0,.2,1)}.mdc-button--raised:focus,.mdc-button--raised:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.mdc-button--raised:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.mdc-button--raised:disabled{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)}.mdc-button--outlined{border-style:solid;padding:0 14px;border-width:2px}.mdc-button--outlined:disabled{border-color:rgba(0,0,0,.37)}.mdc-button--outlined:not(:disabled){border-color:#6200ee;border-color:var(--mdc-theme-primary,#6200ee)}.mdc-button--dense{height:32px;font-size:.8125rem}.mdc-button{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-button:after,.mdc-button:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:\"\"}.mdc-button:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}.mdc-button.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}.mdc-button.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-button.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}.mdc-button.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-button.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}.mdc-button:after,.mdc-button:before{top:-50%;left:-50%;width:200%;height:200%}.mdc-button.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}.mdc-button:after,.mdc-button:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){.mdc-button:after,.mdc-button:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}.mdc-button:hover:before{opacity:.04}.mdc-button.mdc-ripple-upgraded--background-focused:before,.mdc-button:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-button:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-button:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.mdc-button.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}.mdc-button--raised:after,.mdc-button--raised:before,.mdc-button--unelevated:after,.mdc-button--unelevated:before{background-color:#fff}@supports not (-ms-ime-align:auto){.mdc-button--raised:after,.mdc-button--raised:before,.mdc-button--unelevated:after,.mdc-button--unelevated:before{background-color:#fff;background-color:var(--mdc-theme-on-primary,#fff)}}.mdc-button--raised:hover:before,.mdc-button--unelevated:hover:before{opacity:.08}.mdc-button--raised.mdc-ripple-upgraded--background-focused:before,.mdc-button--raised:not(.mdc-ripple-upgraded):focus:before,.mdc-button--unelevated.mdc-ripple-upgraded--background-focused:before,.mdc-button--unelevated:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}.mdc-button--raised:not(.mdc-ripple-upgraded):after,.mdc-button--unelevated:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mdc-button--raised:not(.mdc-ripple-upgraded):active:after,.mdc-button--unelevated:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}.mdc-button--raised.mdc-ripple-upgraded,.mdc-button--unelevated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.24}\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-list{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;line-height:1.5rem;margin:0;padding:8px 0;list-style-type:none;color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background,rgba(0,0,0,.87))}.mdc-list-item__secondary-text{color:rgba(0,0,0,.54);color:var(--mdc-theme-text-secondary-on-background,rgba(0,0,0,.54))}.mdc-list-item__graphic{background-color:transparent;color:rgba(0,0,0,.38);color:var(--mdc-theme-text-icon-on-background,rgba(0,0,0,.38))}.mdc-list-item__meta{color:rgba(0,0,0,.38);color:var(--mdc-theme-text-hint-on-background,rgba(0,0,0,.38))}.mdc-list-group__subheader{color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background,rgba(0,0,0,.87))}.mdc-list--dense{padding-top:4px;padding-bottom:4px;font-size:.812rem}.mdc-list-item{display:flex;position:relative;align-items:center;justify-content:flex-start;height:48px;padding:0 16px;overflow:hidden}.mdc-list-item:focus{outline:none}.mdc-list-item--activated,.mdc-list-item--activated .mdc-list-item__graphic,.mdc-list-item--selected,.mdc-list-item--selected .mdc-list-item__graphic{color:#6200ee;color:var(--mdc-theme-primary,#6200ee)}.mdc-list-item--disabled{color:rgba(0,0,0,.38);color:var(--mdc-theme-text-disabled-on-background,rgba(0,0,0,.38))}.mdc-list-item__graphic{margin-left:0;margin-right:32px;width:24px;height:24px;flex-shrink:0;align-items:center;justify-content:center;fill:currentColor}.mdc-list-item[dir=rtl] .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list-item__graphic{margin-left:32px;margin-right:0}.mdc-list .mdc-list-item__graphic{display:inline-flex}.mdc-list-item__meta{margin-left:auto;margin-right:0}.mdc-list-item[dir=rtl] .mdc-list-item__meta,[dir=rtl] .mdc-list-item .mdc-list-item__meta{margin-left:0;margin-right:auto}.mdc-list-item__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mdc-list-item__text[for]{pointer-events:none}.mdc-list-item__primary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;margin-top:0;line-height:normal;margin-bottom:-20px;display:block}.mdc-list-item__primary-text:before{display:inline-block;width:0;height:32px;content:\"\";vertical-align:0}.mdc-list-item__primary-text:after{display:inline-block;width:0;height:20px;content:\"\";vertical-align:-20px}.mdc-list--dense .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-list--dense .mdc-list-item__primary-text:before{display:inline-block;width:0;height:24px;content:\"\";vertical-align:0}.mdc-list--dense .mdc-list-item__primary-text:after{display:inline-block;width:0;height:20px;content:\"\";vertical-align:-20px}.mdc-list-item__secondary-text{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:1.25rem;font-weight:400;letter-spacing:.0178571429em;text-decoration:inherit;text-transform:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;margin-top:0;line-height:normal;display:block}.mdc-list-item__secondary-text:before{display:inline-block;width:0;height:20px;content:\"\";vertical-align:0}.mdc-list--dense .mdc-list-item__secondary-text{display:block;margin-top:0;line-height:normal;font-size:inherit}.mdc-list--dense .mdc-list-item__secondary-text:before{display:inline-block;width:0;height:20px;content:\"\";vertical-align:0}.mdc-list--dense .mdc-list-item{height:40px}.mdc-list--dense .mdc-list-item__graphic{margin-left:0;margin-right:36px;width:20px;height:20px}.mdc-list-item[dir=rtl] .mdc-list--dense .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--dense .mdc-list-item__graphic{margin-left:36px;margin-right:0}.mdc-list--avatar-list .mdc-list-item{height:56px}.mdc-list--avatar-list .mdc-list-item__graphic{margin-left:0;margin-right:16px;width:40px;height:40px;border-radius:50%}.mdc-list-item[dir=rtl] .mdc-list--avatar-list .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--avatar-list .mdc-list-item__graphic{margin-left:16px;margin-right:0}.mdc-list--two-line .mdc-list-item__text{align-self:flex-start}.mdc-list--two-line .mdc-list-item{height:72px}.mdc-list--avatar-list.mdc-list--dense .mdc-list-item,.mdc-list--two-line.mdc-list--dense .mdc-list-item{height:60px}.mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic{margin-left:0;margin-right:20px;width:36px;height:36px}.mdc-list-item[dir=rtl] .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic{margin-left:20px;margin-right:0}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item{cursor:pointer}a.mdc-list-item{color:inherit;text-decoration:none}.mdc-list-divider{height:0;margin:0;border:none;border-bottom:1px solid;border-bottom-color:rgba(0,0,0,.12)}.mdc-list-divider--padded{margin:0 16px}.mdc-list-divider--inset{margin-left:72px;margin-right:0;width:calc(100% - 72px)}.mdc-list-group[dir=rtl] .mdc-list-divider--inset,[dir=rtl] .mdc-list-group .mdc-list-divider--inset{margin-left:0;margin-right:72px}.mdc-list-divider--inset.mdc-list-divider--padded{width:calc(100% - 88px)}.mdc-list-group .mdc-list{padding:0}.mdc-list-group__subheader{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;margin:.75rem 16px}@-webkit-keyframes mdc-ripple-fg-radius-in{0%{-webkit-animation-timing-function:cubic-bezier(.4,0,.2,1);animation-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1);transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1)}to{-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}}@keyframes mdc-ripple-fg-radius-in{0%{-webkit-animation-timing-function:cubic-bezier(.4,0,.2,1);animation-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1);transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1)}to{-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}}@-webkit-keyframes mdc-ripple-fg-opacity-in{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}}@keyframes mdc-ripple-fg-opacity-in{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}}@-webkit-keyframes mdc-ripple-fg-opacity-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}to{opacity:0}}@keyframes mdc-ripple-fg-opacity-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}to{opacity:0}}.mdc-ripple-surface--test-edge-var-bug{--mdc-ripple-surface-test-edge-var:1px solid #000;visibility:hidden}.mdc-ripple-surface--test-edge-var-bug:before{border:var(--mdc-ripple-surface-test-edge-var)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:\"\"}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{top:-50%;left:-50%;width:200%;height:200%}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{background-color:#000}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:hover:before{opacity:.04}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:hover:before{opacity:.16}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{opacity:.08}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:hover:before{opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.2}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.2}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.2}.rmwc-icon--image{min-width:1em;min-height:1em;background-repeat:no-repeat;font-size:1.5rem;background-size:1em;background-position:50%}.rmwc-icon--size-xsmall{font-size:1.125rem;width:1em;height:1em}.rmwc-icon--size-small{font-size:1.25rem;width:1em;height:1em}.rmwc-icon--size-medium{font-size:1.5rem;width:1em;height:1em}.rmwc-icon--size-large{font-size:2.25rem;width:1em;height:1em}.rmwc-icon--size-xlarge{font-size:3rem;width:1em;height:1em}\n\n/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/.mdc-drawer{background-color:#fff;border-radius:0 0 0 0;z-index:6;width:256px;display:flex;flex-direction:column;flex-shrink:0;box-sizing:border-box;height:100%;transition-property:-webkit-transform;-webkit-transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);border-right:1px solid;border-color:rgba(0,0,0,.12);overflow:hidden}.mdc-drawer .mdc-drawer__title{color:rgba(0,0,0,.87)}.mdc-drawer .mdc-drawer__subtitle,.mdc-drawer .mdc-list-group__subheader,.mdc-drawer .mdc-list-item__graphic{color:rgba(0,0,0,.6)}.mdc-drawer .mdc-list-item{color:rgba(0,0,0,.87)}.mdc-drawer .mdc-list-item--activated .mdc-list-item__graphic{color:#6200ee}.mdc-drawer .mdc-list-item--activated{color:rgba(98,0,238,.87)}.mdc-drawer[dir=rtl],[dir=rtl] .mdc-drawer{border-radius:0 0 0 0}.mdc-drawer .mdc-list-item{border-radius:4px}.mdc-drawer.mdc-drawer--open:not(.mdc-drawer--closing)+.mdc-drawer-app-content{margin-left:256px;margin-right:0}.mdc-drawer.mdc-drawer--open:not(.mdc-drawer--closing)+.mdc-drawer-app-content[dir=rtl],[dir=rtl] .mdc-drawer.mdc-drawer--open:not(.mdc-drawer--closing)+.mdc-drawer-app-content{margin-left:0;margin-right:256px}.mdc-drawer[dir=rtl],[dir=rtl] .mdc-drawer{border-right-width:0;border-left-width:1px;border-right-style:none;border-left-style:solid}.mdc-drawer .mdc-list-item{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:1.375rem;font-weight:500;letter-spacing:.0071428571em;text-decoration:inherit;text-transform:inherit;height:40px;margin:8px;padding:0 8px}.mdc-drawer .mdc-list-item:first-child{margin-top:2px}.mdc-drawer .mdc-list-item:last-child{margin-bottom:0}.mdc-drawer .mdc-list-group__subheader{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:1.25rem;font-weight:400;letter-spacing:.0178571429em;text-decoration:inherit;text-transform:inherit;display:block;line-height:normal;margin:0;padding:0 16px}.mdc-drawer .mdc-list-group__subheader:before{display:inline-block;width:0;height:24px;content:\"\";vertical-align:0}.mdc-drawer .mdc-list-divider{margin:3px 0 4px}.mdc-drawer .mdc-list-item__graphic,.mdc-drawer .mdc-list-item__text{pointer-events:none}.mdc-drawer--animate{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.mdc-drawer--animate[dir=rtl],[dir=rtl] .mdc-drawer--animate{-webkit-transform:translateX(100%);transform:translateX(100%)}.mdc-drawer--opening{-webkit-transition-duration:.25s;transition-duration:.25s}.mdc-drawer--opening,.mdc-drawer--opening[dir=rtl],[dir=rtl] .mdc-drawer--opening{-webkit-transform:translateX(0);transform:translateX(0)}.mdc-drawer--closing{-webkit-transform:translateX(-100%);transform:translateX(-100%);-webkit-transition-duration:.2s;transition-duration:.2s}.mdc-drawer--closing[dir=rtl],[dir=rtl] .mdc-drawer--closing{-webkit-transform:translateX(100%);transform:translateX(100%)}.mdc-drawer__header{flex-shrink:0;box-sizing:border-box;min-height:64px;padding:0 16px 4px}.mdc-drawer__title{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1.25rem;line-height:2rem;font-weight:500;letter-spacing:.0125em;text-decoration:inherit;text-transform:inherit;display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-drawer__title:before{display:inline-block;width:0;height:36px;content:\"\";vertical-align:0}.mdc-drawer__title:after{display:inline-block;width:0;height:20px;content:\"\";vertical-align:-20px}.mdc-drawer__subtitle{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:1.25rem;font-weight:400;letter-spacing:.0178571429em;text-decoration:inherit;text-transform:inherit;display:block;margin-top:0;line-height:normal;margin-bottom:0}.mdc-drawer__subtitle:before{display:inline-block;width:0;height:20px;content:\"\";vertical-align:0}.mdc-drawer__content{height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch}.mdc-drawer--dismissible{left:0;right:auto;display:none;position:absolute}.mdc-drawer--dismissible[dir=rtl],[dir=rtl] .mdc-drawer--dismissible{left:auto;right:0}.mdc-drawer--dismissible.mdc-drawer--open{display:flex}.mdc-drawer-app-content{position:relative}.mdc-drawer-app-content,.mdc-drawer-app-content[dir=rtl],[dir=rtl] .mdc-drawer-app-content{margin-left:0;margin-right:0}.mdc-drawer--modal{box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);left:0;right:auto;display:none;position:fixed}.mdc-drawer--modal+.mdc-drawer-scrim{background-color:rgba(0,0,0,.32)}.mdc-drawer--modal[dir=rtl],[dir=rtl] .mdc-drawer--modal{left:auto;right:0}.mdc-drawer--modal.mdc-drawer--open{display:flex}.mdc-drawer-scrim{display:none;position:fixed;top:0;left:0;width:100%;height:100%;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1);z-index:5}.mdc-drawer--open+.mdc-drawer-scrim{display:block}.mdc-drawer--animate+.mdc-drawer-scrim{opacity:0}.mdc-drawer--opening+.mdc-drawer-scrim{-webkit-transition-duration:.25s;transition-duration:.25s;opacity:1}.mdc-drawer--closing+.mdc-drawer-scrim{-webkit-transition-duration:.2s;transition-duration:.2s;opacity:0}\n/*# sourceMappingURL=2.6b51f286.chunk.css.map */"
  },
  {
    "path": "ui/web/static/css/main.72fe53e6.chunk.css",
    "content": "@import url(https://fonts.googleapis.com/icon?family=Material+Icons);body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.QueryHistory{height:300px;overflow:scroll}.QueryHistory .mdc-list-item{display:flex}.QueryHistory .query{height:2em;font-family:monospace;white-space:nowrap;flex:1 1}.QueryHistory .time{flex-shrink:0}.QueryHistory .language,.QueryHistory .status{margin:0 16px}main{display:flex;flex-direction:column}.graph{width:100%;flex:1 1}.graph svg{height:100%;width:100%}.App{height:100vh;width:100vw;background:#eee;box-sizing:border-box;overflow:hidden;display:flex;flex-direction:row}.App .Logo{height:2rem;margin-right:8px;position:relative;top:.5rem;display:inline-block}.App>main{flex:1 1;overflow:hidden;height:100%;box-sizing:border-box}.App>main .mdc-typography--headline6{margin:0 30px;padding:12px 0;display:block}.App>main .actions{background:#fff;padding:8px 24px;border-color:#d3d3d3;border-style:solid;border-width:1px 0}.App main .actions>.mdc-button{margin-right:24px}.App main .actions>.mdc-button:not(:first-child){margin-left:24px}\n/*# sourceMappingURL=main.72fe53e6.chunk.css.map */"
  },
  {
    "path": "ui/web/static/js/2.7d84b3fa.chunk.js",
    "content": "(this[\"webpackJsonpcayley-ui\"]=this[\"webpackJsonpcayley-ui\"]||[]).push([[2],[function(e,t,n){\"use strict\";e.exports=n(48)},function(e,t,n){\"use strict\";n.d(t,\"b\",function(){return o}),n.d(t,\"a\",function(){return i}),n.d(t,\"d\",function(){return a}),n.d(t,\"c\",function(){return l});var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function o(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)};function a(e){var t=\"function\"===typeof Symbol&&e[Symbol.iterator],n=0;return t?t.call(e):{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}function l(e,t){var n=\"function\"===typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(l){o={error:l}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a}},function(e,t,n){\"use strict\";n.d(t,\"b\",function(){return l});var r=n(37),o=n.n(r),i=n(8),a=n.n(i);n.d(t,\"a\",function(){return a.a});var l=o.a},function(e,t,n){e.exports=n(61)()},function(e,t,n){\"use strict\";function r(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e)){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(u){o=!0,i=u}finally{try{r||null==l.return||l.return()}finally{if(o)throw i}}return n}}(e,t)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}()}n.d(t,\"a\",function(){return r})},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return p});var r=n(0),o=n(23),i=n(2),a=n(6),l=n(13),u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},s=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},c=function(e,t,n){return\"auto\"===(t=t||n||\"auto\")?function(e){return\"string\"===typeof e&&e.includes(\"/\")?\"url\":r.isValidElement(e)?\"component\":\"ligature\"}(e):t},f={ligature:function(e){var t=e.content,n=s(e,[\"content\"]);return r.createElement(d,u({},n),t)},className:function(e){e.content;var t=s(e,[\"content\"]);return r.createElement(d,u({},t))},url:function(e){var t=e.content,n=s(e,[\"content\"]);return r.createElement(d,u({},n,{className:Object(i.a)(n.className,\"rmwc-icon--image\"),style:u({},n.style,{backgroundImage:\"url(\"+t+\")\"})}))},component:function(e){var t=e.content,n=s(e,[\"content\"]);if(\"svg\"===t.type){var o=t.props,i=o.children,a=s(o,[\"children\"]);return r.createElement(d,u({},a,n,{tag:\"svg\"}),i)}return r.createElement(d,u({},n),t)},auto:void 0},d=Object(a.a)({displayName:\"IconRoot\",tag:\"i\"}),p=Object(o.a)()(function(e){var t,n=e.icon,o=e.iconOptions,a=e.providerContext,d=s(e,[\"icon\",\"iconOptions\",\"providerContext\"]);if(o){var p=u({content:\"string\"===typeof n?n:\"<MyComponent {...}/>\"},o);Object(l.a)(\"Icon component prop 'iconOptions' is deprecated. You options should now be passed directly to the 'icon' prop. I.E. icon={\"+JSON.stringify(p)+\"}\")}var h=u({},function(e){return r.isValidElement(e)||e&&\"object\"!==typeof e?{icon:e}:e}(n),o),m=h.icon,y=h.strategy,v=h.prefix,g=h.basename,b=h.render,_=h.size,E=s(h,[\"icon\",\"strategy\",\"prefix\",\"basename\",\"render\",\"size\"]),T=a.icon||{},C=T.basename,O=void 0===C?null:C,w=T.prefix,S=void 0===w?null:w,x=T.strategy,I=void 0===x?null:x,A=T.render,k=void 0===A?null:A,P=m,N=c(P,y||null,I||null),L=void 0===g?O:g,R=\"className\"===N&&\"string\"===typeof m?\"\"+String(v||S)+m:null,D=\"custom\"===N?b||k:!!N&&f[N]||null;if(!D)return console.error(\"Icon: rendering not implemented for \"+String(N)+\".\"),null;var M=D(u({},d,E,{content:P,className:Object(i.a)(\"rmwc-icon\",L,d.className,E.className,R,(t={},t[\"rmwc-icon--size-\"+(_||\"\")]=!!_,t))}));return M.props.children&&M.props.children.type&&[\"Avatar\",\"Icon\"].includes(M.props.children.type.displayName)?r.cloneElement(M.props.children,u({},M.props.children.props,M.props,{children:M.props.children.props.children,className:Object(i.a)(M.props.className,M.props.children.props.className)})):M});p.displayName=\"Icon\"},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return d});var r=n(0),o=n(8),i=n.n(o),a=n(19),l=n(13),u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},s=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},c=function(e,t){var n=\"function\"===typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(l){o={error:l}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a},f=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(c(arguments[t]));return e},d=function(e){var t=e.displayName,n=e.classNames,o=void 0===n?[]:n,c=e.tag,d=void 0===c?\"div\":c,p=e.deprecate,h=e.defaultProps,m=e.consumeProps,y=void 0===m?[]:m,v=e.render,g=r.forwardRef(function(e,n){var c=e.className,h=e.theme,m=e.tag,g=s(e,[\"className\",\"theme\",\"tag\"]);!function(e,t,n,r){var o=i.a.apply(void 0,f([n],r?Object(a.a)(r):[],\"function\"===typeof t?t(e):t));e.className=o}(g,o,c,h);var b=function(e,t,n){return\"string\"!==typeof t?(e.tag=n,t):n||t}(g,d,m);p&&(g=Object(l.b)(g,p,t)),function(e,t){t.forEach(function(t){delete e[t]})}(g,y);var _=g;return v?v(_,n,b):r.createElement(b,u({},_,{ref:n}))});return g.displayName=t,g.defaultProps=h,g}},function(e,t,n){\"use strict\";var r={};n.r(r),n.d(r,\"supportsCssVariables\",function(){return d}),n.d(r,\"getNormalizedEventCoords\",function(){return p});var o,i=n(0),a=n(20),l=n(1),u=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),s={BG_FOCUSED:\"mdc-ripple-upgraded--background-focused\",FG_ACTIVATION:\"mdc-ripple-upgraded--foreground-activation\",FG_DEACTIVATION:\"mdc-ripple-upgraded--foreground-deactivation\",ROOT:\"mdc-ripple-upgraded\",UNBOUNDED:\"mdc-ripple-upgraded--unbounded\"},c={VAR_FG_SCALE:\"--mdc-ripple-fg-scale\",VAR_FG_SIZE:\"--mdc-ripple-fg-size\",VAR_FG_TRANSLATE_END:\"--mdc-ripple-fg-translate-end\",VAR_FG_TRANSLATE_START:\"--mdc-ripple-fg-translate-start\",VAR_LEFT:\"--mdc-ripple-left\",VAR_TOP:\"--mdc-ripple-top\"},f={DEACTIVATION_TIMEOUT_MS:225,FG_DEACTIVATION_MS:150,INITIAL_ORIGIN_SCALE:.6,PADDING:10,TAP_DELAY_MS:300};function d(e,t){void 0===t&&(t=!1);var n=e.CSS,r=o;if(\"boolean\"===typeof o&&!t)return o;if(!(n&&\"function\"===typeof n.supports))return!1;var i=n.supports(\"--css-vars\",\"yes\"),a=n.supports(\"(--css-vars: yes)\")&&n.supports(\"color\",\"#00000000\");return r=!(!i&&!a)&&!function(e){var t=e.document,n=t.createElement(\"div\");n.className=\"mdc-ripple-surface--test-edge-var-bug\",t.body.appendChild(n);var r=e.getComputedStyle(n),o=null!==r&&\"solid\"===r.borderTopStyle;return n.parentNode&&n.parentNode.removeChild(n),o}(e),t||(o=r),r}function p(e,t,n){if(!e)return{x:0,y:0};var r,o,i=t.x,a=t.y,l=i+n.left,u=a+n.top;if(\"touchstart\"===e.type){var s=e;r=s.changedTouches[0].pageX-l,o=s.changedTouches[0].pageY-u}else{var c=e;r=c.pageX-l,o=c.pageY-u}return{x:r,y:o}}var h,m=[\"touchstart\",\"pointerdown\",\"mousedown\",\"keydown\"],y=[\"touchend\",\"pointerup\",\"mouseup\",\"contextmenu\"],v=[],g=function(e){function t(n){var r=e.call(this,l.a({},t.defaultAdapter,n))||this;return r.activationAnimationHasEnded_=!1,r.activationTimer_=0,r.fgDeactivationRemovalTimer_=0,r.fgScale_=\"0\",r.frame_={width:0,height:0},r.initialSize_=0,r.layoutFrame_=0,r.maxRadius_=0,r.unboundedCoords_={left:0,top:0},r.activationState_=r.defaultActivationState_(),r.activationTimerCallback_=function(){r.activationAnimationHasEnded_=!0,r.runDeactivationUXLogicIfReady_()},r.activateHandler_=function(e){return r.activate_(e)},r.deactivateHandler_=function(){return r.deactivate_()},r.focusHandler_=function(){return r.handleFocus()},r.blurHandler_=function(){return r.handleBlur()},r.resizeHandler_=function(){return r.layout()},r}return l.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return s},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return c},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return f},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},browserSupportsCssVars:function(){return!0},computeBoundingRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},containsEventTarget:function(){return!0},deregisterDocumentInteractionHandler:function(){},deregisterInteractionHandler:function(){},deregisterResizeHandler:function(){},getWindowPageOffset:function(){return{x:0,y:0}},isSurfaceActive:function(){return!0},isSurfaceDisabled:function(){return!0},isUnbounded:function(){return!0},registerDocumentInteractionHandler:function(){},registerInteractionHandler:function(){},registerResizeHandler:function(){},removeClass:function(){},updateCssVariable:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){var e=this,n=this.supportsPressRipple_();if(this.registerRootHandlers_(n),n){var r=t.cssClasses,o=r.ROOT,i=r.UNBOUNDED;requestAnimationFrame(function(){e.adapter_.addClass(o),e.adapter_.isUnbounded()&&(e.adapter_.addClass(i),e.layoutInternal_())})}},t.prototype.destroy=function(){var e=this;if(this.supportsPressRipple_()){this.activationTimer_&&(clearTimeout(this.activationTimer_),this.activationTimer_=0,this.adapter_.removeClass(t.cssClasses.FG_ACTIVATION)),this.fgDeactivationRemovalTimer_&&(clearTimeout(this.fgDeactivationRemovalTimer_),this.fgDeactivationRemovalTimer_=0,this.adapter_.removeClass(t.cssClasses.FG_DEACTIVATION));var n=t.cssClasses,r=n.ROOT,o=n.UNBOUNDED;requestAnimationFrame(function(){e.adapter_.removeClass(r),e.adapter_.removeClass(o),e.removeCssVars_()})}this.deregisterRootHandlers_(),this.deregisterDeactivationHandlers_()},t.prototype.activate=function(e){this.activate_(e)},t.prototype.deactivate=function(){this.deactivate_()},t.prototype.layout=function(){var e=this;this.layoutFrame_&&cancelAnimationFrame(this.layoutFrame_),this.layoutFrame_=requestAnimationFrame(function(){e.layoutInternal_(),e.layoutFrame_=0})},t.prototype.setUnbounded=function(e){var n=t.cssClasses.UNBOUNDED;e?this.adapter_.addClass(n):this.adapter_.removeClass(n)},t.prototype.handleFocus=function(){var e=this;requestAnimationFrame(function(){return e.adapter_.addClass(t.cssClasses.BG_FOCUSED)})},t.prototype.handleBlur=function(){var e=this;requestAnimationFrame(function(){return e.adapter_.removeClass(t.cssClasses.BG_FOCUSED)})},t.prototype.supportsPressRipple_=function(){return this.adapter_.browserSupportsCssVars()},t.prototype.defaultActivationState_=function(){return{activationEvent:void 0,hasDeactivationUXRun:!1,isActivated:!1,isProgrammatic:!1,wasActivatedByPointer:!1,wasElementMadeActive:!1}},t.prototype.registerRootHandlers_=function(e){var t=this;e&&(m.forEach(function(e){t.adapter_.registerInteractionHandler(e,t.activateHandler_)}),this.adapter_.isUnbounded()&&this.adapter_.registerResizeHandler(this.resizeHandler_)),this.adapter_.registerInteractionHandler(\"focus\",this.focusHandler_),this.adapter_.registerInteractionHandler(\"blur\",this.blurHandler_)},t.prototype.registerDeactivationHandlers_=function(e){var t=this;\"keydown\"===e.type?this.adapter_.registerInteractionHandler(\"keyup\",this.deactivateHandler_):y.forEach(function(e){t.adapter_.registerDocumentInteractionHandler(e,t.deactivateHandler_)})},t.prototype.deregisterRootHandlers_=function(){var e=this;m.forEach(function(t){e.adapter_.deregisterInteractionHandler(t,e.activateHandler_)}),this.adapter_.deregisterInteractionHandler(\"focus\",this.focusHandler_),this.adapter_.deregisterInteractionHandler(\"blur\",this.blurHandler_),this.adapter_.isUnbounded()&&this.adapter_.deregisterResizeHandler(this.resizeHandler_)},t.prototype.deregisterDeactivationHandlers_=function(){var e=this;this.adapter_.deregisterInteractionHandler(\"keyup\",this.deactivateHandler_),y.forEach(function(t){e.adapter_.deregisterDocumentInteractionHandler(t,e.deactivateHandler_)})},t.prototype.removeCssVars_=function(){var e=this,n=t.strings;Object.keys(n).forEach(function(t){0===t.indexOf(\"VAR_\")&&e.adapter_.updateCssVariable(n[t],null)})},t.prototype.activate_=function(e){var t=this;if(!this.adapter_.isSurfaceDisabled()){var n=this.activationState_;if(!n.isActivated){var r=this.previousActivationEvent_;if(!(r&&void 0!==e&&r.type!==e.type))n.isActivated=!0,n.isProgrammatic=void 0===e,n.activationEvent=e,n.wasActivatedByPointer=!n.isProgrammatic&&(void 0!==e&&(\"mousedown\"===e.type||\"touchstart\"===e.type||\"pointerdown\"===e.type)),void 0!==e&&v.length>0&&v.some(function(e){return t.adapter_.containsEventTarget(e)})?this.resetActivationState_():(void 0!==e&&(v.push(e.target),this.registerDeactivationHandlers_(e)),n.wasElementMadeActive=this.checkElementMadeActive_(e),n.wasElementMadeActive&&this.animateActivation_(),requestAnimationFrame(function(){v=[],n.wasElementMadeActive||void 0===e||\" \"!==e.key&&32!==e.keyCode||(n.wasElementMadeActive=t.checkElementMadeActive_(e),n.wasElementMadeActive&&t.animateActivation_()),n.wasElementMadeActive||(t.activationState_=t.defaultActivationState_())}))}}},t.prototype.checkElementMadeActive_=function(e){return void 0===e||\"keydown\"!==e.type||this.adapter_.isSurfaceActive()},t.prototype.animateActivation_=function(){var e=this,n=t.strings,r=n.VAR_FG_TRANSLATE_START,o=n.VAR_FG_TRANSLATE_END,i=t.cssClasses,a=i.FG_DEACTIVATION,l=i.FG_ACTIVATION,u=t.numbers.DEACTIVATION_TIMEOUT_MS;this.layoutInternal_();var s=\"\",c=\"\";if(!this.adapter_.isUnbounded()){var f=this.getFgTranslationCoordinates_(),d=f.startPoint,p=f.endPoint;s=d.x+\"px, \"+d.y+\"px\",c=p.x+\"px, \"+p.y+\"px\"}this.adapter_.updateCssVariable(r,s),this.adapter_.updateCssVariable(o,c),clearTimeout(this.activationTimer_),clearTimeout(this.fgDeactivationRemovalTimer_),this.rmBoundedActivationClasses_(),this.adapter_.removeClass(a),this.adapter_.computeBoundingRect(),this.adapter_.addClass(l),this.activationTimer_=setTimeout(function(){return e.activationTimerCallback_()},u)},t.prototype.getFgTranslationCoordinates_=function(){var e,t=this.activationState_,n=t.activationEvent;return{startPoint:e={x:(e=t.wasActivatedByPointer?p(n,this.adapter_.getWindowPageOffset(),this.adapter_.computeBoundingRect()):{x:this.frame_.width/2,y:this.frame_.height/2}).x-this.initialSize_/2,y:e.y-this.initialSize_/2},endPoint:{x:this.frame_.width/2-this.initialSize_/2,y:this.frame_.height/2-this.initialSize_/2}}},t.prototype.runDeactivationUXLogicIfReady_=function(){var e=this,n=t.cssClasses.FG_DEACTIVATION,r=this.activationState_,o=r.hasDeactivationUXRun,i=r.isActivated;(o||!i)&&this.activationAnimationHasEnded_&&(this.rmBoundedActivationClasses_(),this.adapter_.addClass(n),this.fgDeactivationRemovalTimer_=setTimeout(function(){e.adapter_.removeClass(n)},f.FG_DEACTIVATION_MS))},t.prototype.rmBoundedActivationClasses_=function(){var e=t.cssClasses.FG_ACTIVATION;this.adapter_.removeClass(e),this.activationAnimationHasEnded_=!1,this.adapter_.computeBoundingRect()},t.prototype.resetActivationState_=function(){var e=this;this.previousActivationEvent_=this.activationState_.activationEvent,this.activationState_=this.defaultActivationState_(),setTimeout(function(){return e.previousActivationEvent_=void 0},t.numbers.TAP_DELAY_MS)},t.prototype.deactivate_=function(){var e=this,t=this.activationState_;if(t.isActivated){var n=l.a({},t);t.isProgrammatic?(requestAnimationFrame(function(){return e.animateDeactivation_(n)}),this.resetActivationState_()):(this.deregisterDeactivationHandlers_(),requestAnimationFrame(function(){e.activationState_.hasDeactivationUXRun=!0,e.animateDeactivation_(n),e.resetActivationState_()}))}},t.prototype.animateDeactivation_=function(e){var t=e.wasActivatedByPointer,n=e.wasElementMadeActive;(t||n)&&this.runDeactivationUXLogicIfReady_()},t.prototype.layoutInternal_=function(){var e=this;this.frame_=this.adapter_.computeBoundingRect();var n=Math.max(this.frame_.height,this.frame_.width);this.maxRadius_=this.adapter_.isUnbounded()?n:Math.sqrt(Math.pow(e.frame_.width,2)+Math.pow(e.frame_.height,2))+t.numbers.PADDING,this.initialSize_=Math.floor(n*t.numbers.INITIAL_ORIGIN_SCALE),this.fgScale_=\"\"+this.maxRadius_/this.initialSize_,this.updateLayoutCssVars_()},t.prototype.updateLayoutCssVars_=function(){var e=t.strings,n=e.VAR_FG_SIZE,r=e.VAR_LEFT,o=e.VAR_TOP,i=e.VAR_FG_SCALE;this.adapter_.updateCssVariable(n,this.initialSize_+\"px\"),this.adapter_.updateCssVariable(i,this.fgScale_),this.adapter_.isUnbounded()&&(this.unboundedCoords_={left:Math.round(this.frame_.width/2-this.initialSize_/2),top:Math.round(this.frame_.height/2-this.initialSize_/2)},this.adapter_.updateCssVariable(r,this.unboundedCoords_.left+\"px\"),this.adapter_.updateCssVariable(o,this.unboundedCoords_.top+\"px\"))},t}(u),b=n(98);function _(e,t){if(void 0===e&&(e=window),void 0===t&&(t=!1),void 0===h||t){var n=!1;try{e.document.addEventListener(\"test\",function(){},{get passive(){return n=!0}})}catch(r){}h=n}return!!h&&{passive:!0}}var E=n(2),T=n(36),C=n(13),O=n(23);n.d(t,\"a\",function(){return k}),n.d(t,\"b\",function(){return P});var w=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),S=function(){return(S=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},x=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},I=i.createContext({}),A=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.surface=n.createElement(\"surface\"),n.isTouched=!1,n.handleFocus=n.handleFocus.bind(n),n.handleBlur=n.handleBlur.bind(n),n.handleMouseDown=n.handleMouseDown.bind(n),n.handleMouseUp=n.handleMouseUp.bind(n),n.handleTouchStart=n.handleTouchStart.bind(n),n.handleTouchEnd=n.handleTouchEnd.bind(n),n.handleKeyDown=n.handleKeyDown.bind(n),n.handleKeyUp=n.handleKeyUp.bind(n),n}return w(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new g({browserSupportsCssVars:function(){return r.supportsCssVariables(window)},isUnbounded:function(){return!!e.props.unbounded},isSurfaceActive:function(){return!!e.root.ref&&Object(b.b)(e.root.ref,\":active\")},isSurfaceDisabled:function(){return!!e.props.disabled},addClass:function(t){return e.surface.addClass(t)},removeClass:function(t){return e.surface.removeClass(t)},containsEventTarget:function(t){return!!e.root.ref&&e.root.ref.contains(t)},registerInteractionHandler:function(t,n){return e.root.addEventListener(t,n)},deregisterInteractionHandler:function(t,n){return e.root.removeEventListener(t,n)},registerDocumentInteractionHandler:function(e,t){return document.documentElement.addEventListener(e,t,_())},deregisterDocumentInteractionHandler:function(e,t){return document.documentElement.removeEventListener(e,t,_())},registerResizeHandler:function(e){return window.addEventListener(\"resize\",e)},deregisterResizeHandler:function(e){return window.removeEventListener(\"resize\",e)},updateCssVariable:function(t,n){return e.surface.setStyle(t,n)},computeBoundingRect:function(){return e.root.ref?e.root.ref.getBoundingClientRect():{width:0,height:0}},getWindowPageOffset:function(){return{x:window.pageXOffset,y:window.pageYOffset}}})},t.prototype.sync=function(e,t){this.root.setRef(a.findDOMNode(this)),e.unbounded!==t.unbounded&&this.foundation.setUnbounded(!!e.unbounded)},t.prototype.handleFocus=function(e){this.props.onFocus&&this.props.onFocus(e),this.foundation&&this.foundation.handleFocus()},t.prototype.handleBlur=function(e){this.props.onBlur&&this.props.onBlur(e),this.foundation.handleBlur()},t.prototype.handleMouseDown=function(e){this.props.onMouseDown&&this.props.onMouseDown(e),this.isTouched||this.activateRipple(e),this.isTouched=!1},t.prototype.handleMouseUp=function(e){this.props.onMouseUp&&this.props.onMouseUp(e),this.deactivateRipple(e)},t.prototype.handleTouchStart=function(e){this.isTouched=!0,this.props.onTouchStart&&this.props.onTouchStart(e),this.activateRipple(e)},t.prototype.handleTouchEnd=function(e){this.props.onTouchEnd&&this.props.onTouchEnd(e),this.deactivateRipple(e)},t.prototype.handleKeyDown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.activateRipple(e)},t.prototype.handleKeyUp=function(e){this.props.onKeyUp&&this.props.onKeyUp(e),this.deactivateRipple(e)},t.prototype.activateRipple=function(e){e.persist(),this.foundation.activate(e)},t.prototype.deactivateRipple=function(e){e.persist(),this.foundation.deactivate()},t.prototype.render=function(){var e=this.props,t=e.children,n=e.className,r=e.primary,o=e.accent,a=e.unbounded,l=e.surface,u=x(e,[\"children\",\"className\",\"primary\",\"accent\",\"unbounded\",\"surface\"]),s=i.Children.only(t);if(!i.isValidElement(s))return null;var c=a?{\"data-mdc-ripple-is-unbounded\":!0}:{},f=!l||!a?this.surface.props({style:s.props.style}):{},d=Object(E.a)(n,f.className,s.props.className,{\"mdc-ripple-surface\":\"boolean\"===typeof l?l:void 0===l,\"mdc-ripple-surface--primary\":r,\"mdc-ripple-surface--accent\":o});u.disabled&&(d=d.replace(\"mdc-ripple-upgraded--background-focused\",\"\"));var p=i.cloneElement(s,S({},s.props,c,this.root.props(S({},u,{style:s.props.style},f,{className:d})),{onFocus:this.handleFocus,onBlur:this.handleBlur,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,onTouchStart:this.handleTouchStart,onTouchEnd:this.handleTouchEnd,onKeyDown:this.handleKeyDown,onKeyUp:this.handleKeyUp}));return i.createElement(I.Provider,{value:this.surface.props({style:s.props.style})},p)},t.shouldDebounce=!0,t.displayName=\"Ripple\",t}(T.a),k=function(e){var t=e.className,n=x(e,[\"className\"]);return i.createElement(I.Consumer,null,function(e){return i.createElement(\"div\",S({},n,e,{className:t+\" \"+(e.className||\"\")}))})},P=function(e){var t=void 0===e?{}:e,n=t.unbounded,r=t.accent,o=t.surface;return function(e){var t=Object(O.a)()(i.forwardRef(function(t,a){var l=t.providerContext,u=t.ripple,s=void 0===u?l.ripple:u,c=x(t,[\"providerContext\",\"ripple\"]),f=\"object\"!==typeof s?{}:s;return(c.accent||c.unbounded||c.surface)&&(Object(C.a)(\"'accent', 'unbounded', and 'surface' have been deprecated as indiviudal props. Please pass an options object to the ripple prop directly. ripple={{accent: true, unbounded: true}} \"),f.accent=c.accent||f.accent,f.unbounded=c.unbounded||f.unbounded,f.surface=c.surface||f.surface),s?i.createElement(A,S({},c,{accent:f.accent||r,unbounded:f.unbounded||n,surface:f.surface||o}),i.createElement(e,S({},c,{ref:a}))):i.createElement(e,S({},c,{ref:a}))}));return t.displayName=\"withRipple(\"+(e.displayName||\"Unknown\")+\")\",t}}},function(e,t,n){var r;!function(){\"use strict\";var n={}.hasOwnProperty;function o(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var i=typeof r;if(\"string\"===i||\"number\"===i)e.push(r);else if(Array.isArray(r)&&r.length){var a=o.apply(null,r);a&&e.push(a)}else if(\"object\"===i)for(var l in r)n.call(r,l)&&r[l]&&e.push(l)}}return e.join(\" \")}e.exports?(o.default=o,e.exports=o):void 0===(r=function(){return o}.apply(t,[]))||(e.exports=r)}()},function(e,t,n){e.exports=n(58)},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),Object.defineProperty(t,\"DiffEditor\",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,\"ControlledEditor\",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,\"monaco\",{enumerable:!0,get:function(){return a.monaco}}),t.default=void 0;var r=l(n(59)),o=l(n(71)),i=l(n(74)),a=n(16);function l(e){return e&&e.__esModule?e:{default:e}}var u=r.default;t.default=u},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return r});var r=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}()},function(e,t,n){\"use strict\";function r(e,t,n,r,o,i,a){try{var l=e[i](a),u=l.value}catch(s){return void n(s)}l.done?t(u):Promise.resolve(u).then(r,o)}function o(e){return function(){var t=this,n=arguments;return new Promise(function(o,i){var a=e.apply(t,n);function l(e){r(a,o,i,l,u,\"next\",e)}function u(e){r(a,o,i,l,u,\"throw\",e)}l(void 0)})}}n.d(t,\"a\",function(){return o})},function(e,t,n){\"use strict\";(function(e){n.d(t,\"a\",function(){return o}),n.d(t,\"b\",function(){return i});var r=function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},o=function(t){e&&Object({NODE_ENV:\"production\",PUBLIC_URL:\"\",REACT_APP_SERVER_URL:\"http://localhost:64210\"}),0},i=function(e,t,n){for(var i in e=r({},e),t){var a=t[i],l=void 0,u=function(e){return e};if(Array.isArray(a)?(l=a[0],u=a[1]):l=a,void 0!==e[i]){if(\"\"===l)o((n||\"\")+\" component prop '\"+i+\"' has been removed from and is no longer a valid prop.\");else{e[l]=u(e[i]);var s=\"\";e[l]!==e[i]&&(s=\" The old value has also been converted from '\"+e[i]+\"' to '\"+e[l]+\"'\"),o((n||\"\")+\" component prop '\"+i+\"' has been replaced with '\"+l+\"'. \"+s)}delete e[i]}}return e}}).call(this,n(78))},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return s});var r=n(0),o=n(6),i=n(7),a=n(5),l=function(){return(l=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},u=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},s=Object(i.b)({surface:!1})(Object(o.a)({displayName:\"Button\",tag:\"button\",classNames:function(e){return[\"mdc-button\",{\"mdc-button--dense\":e.dense,\"mdc-button--raised\":e.raised,\"mdc-button--unelevated\":e.unelevated,\"mdc-button--outlined\":e.outlined}]},consumeProps:[\"dense\",\"unelevated\",\"outlined\",\"primary\",\"accent\",\"unbounded\"],render:function(e,t,n){var o=e.icon,i=e.trailingIcon,a=e.label,s=e.children,f=(e.raised,e.danger),d=u(e,[\"icon\",\"trailingIcon\",\"label\",\"children\",\"raised\",\"danger\"]);if(f){var p=d.style||{};d.style=l({},{\"--mdc-theme-primary\":\"var(--mdc-theme-error)\",\"--mdc-theme-on-primary\":\"var(--mdc-theme-on-error)\"},p)}return r.createElement(n,l({},d,{ref:t}),!!o&&r.createElement(c,{icon:o}),r.createElement(\"span\",{className:\"mdc-button__label\"},a,s),!!i&&r.createElement(c,{icon:i}))}})),c=Object(o.a)({displayName:\"ButtonIcon\",tag:a.a,classNames:[\"mdc-button__icon\"]})},function(e,t,n){\"use strict\";function r(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}(e)||function(e){if(Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance\")}()}n.d(t,\"a\",function(){return r})},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),Object.defineProperty(t,\"monaco\",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(t,\"noop\",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,\"deepMerge\",{enumerable:!0,get:function(){return i.default}});var r=a(n(64)),o=a(n(66)),i=a(n(67));function a(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){\"use strict\";var r=function(e,t){var n;function r(){var r,o,i=n.length,a=0,l=0;for(r=0;r<i;++r)a+=(o=n[r]).x,l+=o.y;for(a=a/i-e,l=l/i-t,r=0;r<i;++r)(o=n[r]).x-=a,o.y-=l}return null==e&&(e=0),null==t&&(t=0),r.initialize=function(e){n=e},r.x=function(t){return arguments.length?(e=+t,r):e},r.y=function(e){return arguments.length?(t=+e,r):t},r};function o(e,t,n,r){if(isNaN(t)||isNaN(n))return e;var o,i,a,l,u,s,c,f,d,p=e._root,h={data:r},m=e._x0,y=e._y0,v=e._x1,g=e._y1;if(!p)return e._root=h,e;for(;p.length;)if((s=t>=(i=(m+v)/2))?m=i:v=i,(c=n>=(a=(y+g)/2))?y=a:g=a,o=p,!(p=p[f=c<<1|s]))return o[f]=h,e;if(l=+e._x.call(null,p.data),u=+e._y.call(null,p.data),t===l&&n===u)return h.next=p,o?o[f]=h:e._root=h,e;do{o=o?o[f]=new Array(4):e._root=new Array(4),(s=t>=(i=(m+v)/2))?m=i:v=i,(c=n>=(a=(y+g)/2))?y=a:g=a}while((f=c<<1|s)===(d=(u>=a)<<1|l>=i));return o[d]=p,o[f]=h,e}var i=function(e,t,n,r,o){this.node=e,this.x0=t,this.y0=n,this.x1=r,this.y1=o};function a(e){return e[0]}function l(e){return e[1]}function u(e,t,n){var r=new s(null==t?a:t,null==n?l:n,NaN,NaN,NaN,NaN);return null==e?r:r.addAll(e)}function s(e,t,n,r,o,i){this._x=e,this._y=t,this._x0=n,this._y0=r,this._x1=o,this._y1=i,this._root=void 0}function c(e){for(var t={data:e.data},n=t;e=e.next;)n=n.next={data:e.data};return t}var f=u.prototype=s.prototype;f.copy=function(){var e,t,n=new s(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=c(r),n;for(e=[{source:r,target:n._root=new Array(4)}];r=e.pop();)for(var o=0;o<4;++o)(t=r.source[o])&&(t.length?e.push({source:t,target:r.target[o]=new Array(4)}):r.target[o]=c(t));return n},f.add=function(e){var t=+this._x.call(null,e),n=+this._y.call(null,e);return o(this.cover(t,n),t,n,e)},f.addAll=function(e){var t,n,r,i,a=e.length,l=new Array(a),u=new Array(a),s=1/0,c=1/0,f=-1/0,d=-1/0;for(n=0;n<a;++n)isNaN(r=+this._x.call(null,t=e[n]))||isNaN(i=+this._y.call(null,t))||(l[n]=r,u[n]=i,r<s&&(s=r),r>f&&(f=r),i<c&&(c=i),i>d&&(d=i));if(s>f||c>d)return this;for(this.cover(s,c).cover(f,d),n=0;n<a;++n)o(this,l[n],u[n],e[n]);return this},f.cover=function(e,t){if(isNaN(e=+e)||isNaN(t=+t))return this;var n=this._x0,r=this._y0,o=this._x1,i=this._y1;if(isNaN(n))o=(n=Math.floor(e))+1,i=(r=Math.floor(t))+1;else{for(var a,l,u=o-n,s=this._root;n>e||e>=o||r>t||t>=i;)switch(l=(t<r)<<1|e<n,(a=new Array(4))[l]=s,s=a,u*=2,l){case 0:o=n+u,i=r+u;break;case 1:n=o-u,i=r+u;break;case 2:o=n+u,r=i-u;break;case 3:n=o-u,r=i-u}this._root&&this._root.length&&(this._root=s)}return this._x0=n,this._y0=r,this._x1=o,this._y1=i,this},f.data=function(){var e=[];return this.visit(function(t){if(!t.length)do{e.push(t.data)}while(t=t.next)}),e},f.extent=function(e){return arguments.length?this.cover(+e[0][0],+e[0][1]).cover(+e[1][0],+e[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},f.find=function(e,t,n){var r,o,a,l,u,s,c,f=this._x0,d=this._y0,p=this._x1,h=this._y1,m=[],y=this._root;for(y&&m.push(new i(y,f,d,p,h)),null==n?n=1/0:(f=e-n,d=t-n,p=e+n,h=t+n,n*=n);s=m.pop();)if(!(!(y=s.node)||(o=s.x0)>p||(a=s.y0)>h||(l=s.x1)<f||(u=s.y1)<d))if(y.length){var v=(o+l)/2,g=(a+u)/2;m.push(new i(y[3],v,g,l,u),new i(y[2],o,g,v,u),new i(y[1],v,a,l,g),new i(y[0],o,a,v,g)),(c=(t>=g)<<1|e>=v)&&(s=m[m.length-1],m[m.length-1]=m[m.length-1-c],m[m.length-1-c]=s)}else{var b=e-+this._x.call(null,y.data),_=t-+this._y.call(null,y.data),E=b*b+_*_;if(E<n){var T=Math.sqrt(n=E);f=e-T,d=t-T,p=e+T,h=t+T,r=y.data}}return r},f.remove=function(e){if(isNaN(i=+this._x.call(null,e))||isNaN(a=+this._y.call(null,e)))return this;var t,n,r,o,i,a,l,u,s,c,f,d,p=this._root,h=this._x0,m=this._y0,y=this._x1,v=this._y1;if(!p)return this;if(p.length)for(;;){if((s=i>=(l=(h+y)/2))?h=l:y=l,(c=a>=(u=(m+v)/2))?m=u:v=u,t=p,!(p=p[f=c<<1|s]))return this;if(!p.length)break;(t[f+1&3]||t[f+2&3]||t[f+3&3])&&(n=t,d=f)}for(;p.data!==e;)if(r=p,!(p=p.next))return this;return(o=p.next)&&delete p.next,r?(o?r.next=o:delete r.next,this):t?(o?t[f]=o:delete t[f],(p=t[0]||t[1]||t[2]||t[3])&&p===(t[3]||t[2]||t[1]||t[0])&&!p.length&&(n?n[d]=p:this._root=p),this):(this._root=o,this)},f.removeAll=function(e){for(var t=0,n=e.length;t<n;++t)this.remove(e[t]);return this},f.root=function(){return this._root},f.size=function(){var e=0;return this.visit(function(t){if(!t.length)do{++e}while(t=t.next)}),e},f.visit=function(e){var t,n,r,o,a,l,u=[],s=this._root;for(s&&u.push(new i(s,this._x0,this._y0,this._x1,this._y1));t=u.pop();)if(!e(s=t.node,r=t.x0,o=t.y0,a=t.x1,l=t.y1)&&s.length){var c=(r+a)/2,f=(o+l)/2;(n=s[3])&&u.push(new i(n,c,f,a,l)),(n=s[2])&&u.push(new i(n,r,f,c,l)),(n=s[1])&&u.push(new i(n,c,o,a,f)),(n=s[0])&&u.push(new i(n,r,o,c,f))}return this},f.visitAfter=function(e){var t,n=[],r=[];for(this._root&&n.push(new i(this._root,this._x0,this._y0,this._x1,this._y1));t=n.pop();){var o=t.node;if(o.length){var a,l=t.x0,u=t.y0,s=t.x1,c=t.y1,f=(l+s)/2,d=(u+c)/2;(a=o[0])&&n.push(new i(a,l,u,f,d)),(a=o[1])&&n.push(new i(a,f,u,s,d)),(a=o[2])&&n.push(new i(a,l,d,f,c)),(a=o[3])&&n.push(new i(a,f,d,s,c))}r.push(t)}for(;t=r.pop();)e(t.node,t.x0,t.y0,t.x1,t.y1);return this},f.x=function(e){return arguments.length?(this._x=e,this):this._x},f.y=function(e){return arguments.length?(this._y=e,this):this._y};var d=function(e){return function(){return e}},p=function(){return 1e-6*(Math.random()-.5)};function h(e){return e.index}function m(e,t){var n=e.get(t);if(!n)throw new Error(\"missing: \"+t);return n}var y=function(e){var t,n,r,o,i,a=h,l=function(e){return 1/Math.min(o[e.source.index],o[e.target.index])},u=d(30),s=1;function c(r){for(var o=0,a=e.length;o<s;++o)for(var l,u,c,f,d,h,m,y=0;y<a;++y)u=(l=e[y]).source,f=(c=l.target).x+c.vx-u.x-u.vx||p(),d=c.y+c.vy-u.y-u.vy||p(),f*=h=((h=Math.sqrt(f*f+d*d))-n[y])/h*r*t[y],d*=h,c.vx-=f*(m=i[y]),c.vy-=d*m,u.vx+=f*(m=1-m),u.vy+=d*m}function f(){if(r){var l,u,s=r.length,c=e.length,f=new Map(r.map(function(e,t){return[a(e,t,r),e]}));for(l=0,o=new Array(s);l<c;++l)(u=e[l]).index=l,\"object\"!==typeof u.source&&(u.source=m(f,u.source)),\"object\"!==typeof u.target&&(u.target=m(f,u.target)),o[u.source.index]=(o[u.source.index]||0)+1,o[u.target.index]=(o[u.target.index]||0)+1;for(l=0,i=new Array(c);l<c;++l)u=e[l],i[l]=o[u.source.index]/(o[u.source.index]+o[u.target.index]);t=new Array(c),y(),n=new Array(c),v()}}function y(){if(r)for(var n=0,o=e.length;n<o;++n)t[n]=+l(e[n],n,e)}function v(){if(r)for(var t=0,o=e.length;t<o;++t)n[t]=+u(e[t],t,e)}return null==e&&(e=[]),c.initialize=function(e){r=e,f()},c.links=function(t){return arguments.length?(e=t,f(),c):e},c.id=function(e){return arguments.length?(a=e,c):a},c.iterations=function(e){return arguments.length?(s=+e,c):s},c.strength=function(e){return arguments.length?(l=\"function\"===typeof e?e:d(+e),y(),c):l},c.distance=function(e){return arguments.length?(u=\"function\"===typeof e?e:d(+e),v(),c):u},c},v={value:function(){}};function g(){for(var e,t=0,n=arguments.length,r={};t<n;++t){if(!(e=arguments[t]+\"\")||e in r)throw new Error(\"illegal type: \"+e);r[e]=[]}return new b(r)}function b(e){this._=e}function _(e,t){return e.trim().split(/^|\\s+/).map(function(e){var n=\"\",r=e.indexOf(\".\");if(r>=0&&(n=e.slice(r+1),e=e.slice(0,r)),e&&!t.hasOwnProperty(e))throw new Error(\"unknown type: \"+e);return{type:e,name:n}})}function E(e,t){for(var n,r=0,o=e.length;r<o;++r)if((n=e[r]).name===t)return n.value}function T(e,t,n){for(var r=0,o=e.length;r<o;++r)if(e[r].name===t){e[r]=v,e=e.slice(0,r).concat(e.slice(r+1));break}return null!=n&&e.push({name:t,value:n}),e}b.prototype=g.prototype={constructor:b,on:function(e,t){var n,r=this._,o=_(e+\"\",r),i=-1,a=o.length;if(!(arguments.length<2)){if(null!=t&&\"function\"!==typeof t)throw new Error(\"invalid callback: \"+t);for(;++i<a;)if(n=(e=o[i]).type)r[n]=T(r[n],e.name,t);else if(null==t)for(n in r)r[n]=T(r[n],e.name,null);return this}for(;++i<a;)if((n=(e=o[i]).type)&&(n=E(r[n],e.name)))return n},copy:function(){var e={},t=this._;for(var n in t)e[n]=t[n].slice();return new b(e)},call:function(e,t){if((n=arguments.length-2)>0)for(var n,r,o=new Array(n),i=0;i<n;++i)o[i]=arguments[i+2];if(!this._.hasOwnProperty(e))throw new Error(\"unknown type: \"+e);for(i=0,n=(r=this._[e]).length;i<n;++i)r[i].value.apply(t,o)},apply:function(e,t,n){if(!this._.hasOwnProperty(e))throw new Error(\"unknown type: \"+e);for(var r=this._[e],o=0,i=r.length;o<i;++o)r[o].value.apply(t,n)}};var C,O,w=g,S=0,x=0,I=0,A=1e3,k=0,P=0,N=0,L=\"object\"===typeof performance&&performance.now?performance:Date,R=\"object\"===typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)};function D(){return P||(R(M),P=L.now()+N)}function M(){P=0}function j(){this._call=this._time=this._next=null}function F(e,t,n){var r=new j;return r.restart(e,t,n),r}function U(){P=(k=L.now())+N,S=x=0;try{!function(){D(),++S;for(var e,t=C;t;)(e=P-t._time)>=0&&t._call.call(null,e),t=t._next;--S}()}finally{S=0,function(){var e,t,n=C,r=1/0;for(;n;)n._call?(r>n._time&&(r=n._time),e=n,n=n._next):(t=n._next,n._next=null,n=e?e._next=t:C=t);O=e,V(r)}(),P=0}}function B(){var e=L.now(),t=e-k;t>A&&(N-=t,k=e)}function V(e){S||(x&&(x=clearTimeout(x)),e-P>24?(e<1/0&&(x=setTimeout(U,e-L.now()-N)),I&&(I=clearInterval(I))):(I||(k=L.now(),I=setInterval(B,A)),S=1,R(U)))}j.prototype=F.prototype={constructor:j,restart:function(e,t,n){if(\"function\"!==typeof e)throw new TypeError(\"callback is not a function\");n=(null==n?D():+n)+(null==t?0:+t),this._next||O===this||(O?O._next=this:C=this,O=this),this._call=e,this._time=n,V()},stop:function(){this._call&&(this._call=null,this._time=1/0,V())}};function H(e){return e.x}function z(e){return e.y}var K=10,W=Math.PI*(3-Math.sqrt(5)),G=function(e){var t,n=1,r=.001,o=1-Math.pow(r,1/300),i=0,a=.6,l=new Map,u=F(c),s=w(\"tick\",\"end\");function c(){f(),s.call(\"tick\",t),n<r&&(u.stop(),s.call(\"end\",t))}function f(r){var u,s,c=e.length;void 0===r&&(r=1);for(var f=0;f<r;++f)for(n+=(i-n)*o,l.forEach(function(e){e(n)}),u=0;u<c;++u)null==(s=e[u]).fx?s.x+=s.vx*=a:(s.x=s.fx,s.vx=0),null==s.fy?s.y+=s.vy*=a:(s.y=s.fy,s.vy=0);return t}function d(){for(var t,n=0,r=e.length;n<r;++n){if((t=e[n]).index=n,null!=t.fx&&(t.x=t.fx),null!=t.fy&&(t.y=t.fy),isNaN(t.x)||isNaN(t.y)){var o=K*Math.sqrt(n),i=n*W;t.x=o*Math.cos(i),t.y=o*Math.sin(i)}(isNaN(t.vx)||isNaN(t.vy))&&(t.vx=t.vy=0)}}function p(t){return t.initialize&&t.initialize(e),t}return null==e&&(e=[]),d(),t={tick:f,restart:function(){return u.restart(c),t},stop:function(){return u.stop(),t},nodes:function(n){return arguments.length?(e=n,d(),l.forEach(p),t):e},alpha:function(e){return arguments.length?(n=+e,t):n},alphaMin:function(e){return arguments.length?(r=+e,t):r},alphaDecay:function(e){return arguments.length?(o=+e,t):+o},alphaTarget:function(e){return arguments.length?(i=+e,t):i},velocityDecay:function(e){return arguments.length?(a=1-e,t):1-a},force:function(e,n){return arguments.length>1?(null==n?l.delete(e):l.set(e,p(n)),t):l.get(e)},find:function(t,n,r){var o,i,a,l,u,s=0,c=e.length;for(null==r?r=1/0:r*=r,s=0;s<c;++s)(a=(o=t-(l=e[s]).x)*o+(i=n-l.y)*i)<r&&(u=l,r=a);return u},on:function(e,n){return arguments.length>1?(s.on(e,n),t):s.on(e)}}},q=function(){var e,t,n,r,o=d(-30),i=1,a=1/0,l=.81;function s(r){var o,i=e.length,a=u(e,H,z).visitAfter(f);for(n=r,o=0;o<i;++o)t=e[o],a.visit(h)}function c(){if(e){var t,n,i=e.length;for(r=new Array(i),t=0;t<i;++t)n=e[t],r[n.index]=+o(n,t,e)}}function f(e){var t,n,o,i,a,l=0,u=0;if(e.length){for(o=i=a=0;a<4;++a)(t=e[a])&&(n=Math.abs(t.value))&&(l+=t.value,u+=n,o+=n*t.x,i+=n*t.y);e.x=o/u,e.y=i/u}else{(t=e).x=t.data.x,t.y=t.data.y;do{l+=r[t.data.index]}while(t=t.next)}e.value=l}function h(e,o,u,s){if(!e.value)return!0;var c=e.x-t.x,f=e.y-t.y,d=s-o,h=c*c+f*f;if(d*d/l<h)return h<a&&(0===c&&(h+=(c=p())*c),0===f&&(h+=(f=p())*f),h<i&&(h=Math.sqrt(i*h)),t.vx+=c*e.value*n/h,t.vy+=f*e.value*n/h),!0;if(!(e.length||h>=a)){(e.data!==t||e.next)&&(0===c&&(h+=(c=p())*c),0===f&&(h+=(f=p())*f),h<i&&(h=Math.sqrt(i*h)));do{e.data!==t&&(d=r[e.data.index]*n/h,t.vx+=c*d,t.vy+=f*d)}while(e=e.next)}}return s.initialize=function(t){e=t,c()},s.strength=function(e){return arguments.length?(o=\"function\"===typeof e?e:d(+e),c(),s):o},s.distanceMin=function(e){return arguments.length?(i=e*e,s):Math.sqrt(i)},s.distanceMax=function(e){return arguments.length?(a=e*e,s):Math.sqrt(a)},s.theta=function(e){return arguments.length?(l=e*e,s):Math.sqrt(l)},s};n.d(t,\"a\",function(){return r}),n.d(t,\"b\",function(){return y}),n.d(t,\"c\",function(){return q}),n.d(t,\"d\",function(){return G})},function(e,t,n){\"use strict\";var r=n(0),o=n(6),i=n(36),a=n(1),l=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),u={ANIMATE:\"mdc-drawer--animate\",CLOSING:\"mdc-drawer--closing\",DISMISSIBLE:\"mdc-drawer--dismissible\",MODAL:\"mdc-drawer--modal\",OPEN:\"mdc-drawer--open\",OPENING:\"mdc-drawer--opening\",ROOT:\"mdc-drawer\"},s={APP_CONTENT_SELECTOR:\".mdc-drawer-app-content\",CLOSE_EVENT:\"MDCDrawer:closed\",OPEN_EVENT:\"MDCDrawer:opened\",SCRIM_SELECTOR:\".mdc-drawer-scrim\"},c=function(e){function t(n){var r=e.call(this,a.a({},t.defaultAdapter,n))||this;return r.animationFrame_=0,r.animationTimer_=0,r}return a.b(t,e),Object.defineProperty(t,\"strings\",{get:function(){return s},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"cssClasses\",{get:function(){return u},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},elementHasClass:function(){return!1},notifyClose:function(){},notifyOpen:function(){},saveFocus:function(){},restoreFocus:function(){},focusActiveNavigationItem:function(){},trapFocus:function(){},releaseFocus:function(){}}},enumerable:!0,configurable:!0}),t.prototype.destroy=function(){this.animationFrame_&&cancelAnimationFrame(this.animationFrame_),this.animationTimer_&&clearTimeout(this.animationTimer_)},t.prototype.open=function(){var e=this;this.isOpen()||this.isOpening()||this.isClosing()||(this.adapter_.addClass(u.OPEN),this.adapter_.addClass(u.ANIMATE),this.runNextAnimationFrame_(function(){e.adapter_.addClass(u.OPENING)}),this.adapter_.saveFocus())},t.prototype.close=function(){!this.isOpen()||this.isOpening()||this.isClosing()||this.adapter_.addClass(u.CLOSING)},t.prototype.isOpen=function(){return this.adapter_.hasClass(u.OPEN)},t.prototype.isOpening=function(){return this.adapter_.hasClass(u.OPENING)||this.adapter_.hasClass(u.ANIMATE)},t.prototype.isClosing=function(){return this.adapter_.hasClass(u.CLOSING)},t.prototype.handleKeydown=function(e){var t=e.keyCode;(\"Escape\"===e.key||27===t)&&this.close()},t.prototype.handleTransitionEnd=function(e){var t=u.OPENING,n=u.CLOSING,r=u.OPEN,o=u.ANIMATE,i=u.ROOT;this.isElement_(e.target)&&this.adapter_.elementHasClass(e.target,i)&&(this.isClosing()?(this.adapter_.removeClass(r),this.closed_(),this.adapter_.restoreFocus(),this.adapter_.notifyClose()):(this.adapter_.focusActiveNavigationItem(),this.opened_(),this.adapter_.notifyOpen()),this.adapter_.removeClass(o),this.adapter_.removeClass(t),this.adapter_.removeClass(n))},t.prototype.opened_=function(){},t.prototype.closed_=function(){},t.prototype.runNextAnimationFrame_=function(e){var t=this;cancelAnimationFrame(this.animationFrame_),this.animationFrame_=requestAnimationFrame(function(){t.animationFrame_=0,clearTimeout(t.animationTimer_),t.animationTimer_=setTimeout(e,0)})},t.prototype.isElement_=function(e){return Boolean(e.classList)},t}(l),f=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return a.b(t,e),t.prototype.handleScrimClick=function(){this.close()},t.prototype.opened_=function(){this.adapter_.trapFocus()},t.prototype.closed_=function(){this.adapter_.releaseFocus()},t}(c),d=n(2);n.d(t,\"c\",function(){return y}),n.d(t,\"d\",function(){return v}),n.d(t,\"b\",function(){return g}),n.d(t,\"a\",function(){return O});var p=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),h=function(){return(h=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},m=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},y=Object(o.a)({displayName:\"DrawerHeader\",classNames:[\"mdc-drawer__header\"]}),v=Object(o.a)({displayName:\"DrawerTitle\",classNames:[\"mdc-drawer__title\"]}),g=(Object(o.a)({displayName:\"DrawerSubtitle\",classNames:[\"mdc-drawer__subtitle\"]}),Object(o.a)({displayName:\"DrawerContent\",classNames:[\"mdc-drawer__content\"]})),b=function(e){var t=e.onClick;return r.createElement(\"div\",{className:\"mdc-drawer-scrim\",onClick:t})},_=(Object(o.a)({displayName:\"DrawerAppContent\",classNames:[\"mdc-drawer-app-content\"]}),Object(o.a)({displayName:\"DrawerRoot\",tag:\"aside\",classNames:function(e){return[\"mdc-drawer\",{\"mdc-drawer--dismissible\":e.dismissible,\"mdc-drawer--modal\":e.modal}]},consumeProps:[\"dismissible\",\"modal\"]})),E=function(e,t){var n;return(n=function(t){function n(e){var n=t.call(this,e)||this;return n.root=n.createElement(\"root\"),n.previousFocus=null,n.focusTrap=null,[\"handleScrimClick\",\"handleTransitionEnd\",\"handleKeyDown\"].forEach(function(e){n[e]=n[e].bind(n)}),n}return p(n,t),n.prototype.componentDidMount=function(){t.prototype.componentDidMount.call(this),this.root.ref&&(this.focusTrap=Object(d.b)(this.root.ref,{clickOutsideDeactivates:!0,initialFocus:void 0,escapeDeactivates:!1,returnFocusOnDeactivate:!1}))},n.prototype.getDefaultFoundation=function(){var t=this;return new e({addClass:function(e){return t.root.addClass(e)},removeClass:function(e){return t.root.removeClass(e)},hasClass:function(e){return t.root.hasClass(e)},elementHasClass:function(e,t){return e.classList.contains(t)},saveFocus:function(){t.previousFocus=document.activeElement},restoreFocus:function(){var e=t.previousFocus&&t.previousFocus.focus;t.root.ref&&t.root.ref.contains(document.activeElement)&&e&&t.previousFocus&&t.previousFocus.focus()},focusActiveNavigationItem:function(){var e=t.root.ref&&t.root.ref.querySelector(\".mdc-list-item--activated\");e&&e.focus()},notifyClose:function(){return t.emit(\"onClose\",{},!0)},notifyOpen:function(){return t.emit(\"onOpen\",{},!0)},trapFocus:function(){try{t.focusTrap&&t.focusTrap.activate()}catch(e){}},releaseFocus:function(){try{t.focusTrap&&t.focusTrap.deactivate()}catch(e){}}})},n.prototype.handleScrimClick=function(){\"handleScrimClick\"in this.foundation&&this.foundation.handleScrimClick()},n.prototype.handleKeyDown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.foundation.handleKeydown(e)},n.prototype.handleTransitionEnd=function(e){this.props.onTransitionEnd&&this.props.onTransitionEnd(e),this.foundation.handleTransitionEnd(e)},n.prototype.sync=function(e,t){e.open!==t.open&&(e.open?this.foundation.open():this.foundation.close())},n.prototype.render=function(){var e=this.props,t=(e.onOpen,e.onClose,e.open,m(e,[\"onOpen\",\"onClose\",\"open\"]));return r.createElement(r.Fragment,null,r.createElement(_,h({ref:this.root.setRef},this.root.props(t),{onKeyDown:this.handleKeyDown,onTransitionEnd:this.handleTransitionEnd})),t.modal&&r.createElement(b,{onClick:this.handleScrimClick}))},n}(i.a)).displayName=t,n.defaultProps={open:!1},n},T=E(f,\"ModalDrawer\"),C=E(c,\"dismissibleDrawer\"),O=function(e){return e.dismissible?r.createElement(C,h({},e)):e.modal?r.createElement(T,h({},e)):r.createElement(_,h({},e))};O.displayName=\"Drawer\"},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return i});n(0),n(8);var r=n(22),o=n(13),i=function(e){return\"string\"===typeof e&&e.includes(\" \")&&Object(o.a)(\"Theme no longer accepts a string of theme names with spaces. Please pass them as an array instead.\"),(Array.isArray(e)?e:e.split(\" \")).map(function(e){return e.includes(\"-\")&&Object(o.a)(\"Theme properties need to be passed as camelCase. Please convert \"+e+\" to \"+e.replace(/-([a-z])/g,function(e,t){return t.toUpperCase()})),\"mdc-theme--\"+Object(r.b)(e)})}},function(e,t,n){\"use strict\";!function e(){if(\"undefined\"!==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&\"function\"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}(),e.exports=n(49)},function(e,t,n){\"use strict\";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,\"a\",function(){return r})},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return r}),n.d(t,\"b\",function(){return o});var r=function(e){return e.replace(/(-[a-z])/g,function(e){return e.toUpperCase().replace(\"-\",\"\")})},o=function(e){return e.replace(/([A-Z])/g,function(e){return\"-\"+e.toLowerCase()})}},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return l});var r=n(0),o=(n(13),function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)}),i={ripple:!0,icon:{icon:\"\",basename:\"material-icons\",prefix:\"\",strategy:\"auto\",render:void 0}},a=r.createContext(i),l=function(){return function(e){return r.forwardRef(function(t,n){return r.createElement(a.Consumer,null,function(i){return r.createElement(e,o({},t,{providerContext:i,ref:n}))})})}}},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return o});var r=n(6),o=Object(r.a)({displayName:\"Typography\",tag:\"span\",classNames:function(e){var t;return[(t={},t[\"mdc-typography--\"+e.use]=e.use,t)]},consumeProps:[\"use\"]})},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return i}),n.d(t,\"b\",function(){return a});var r=n(0),o=function(){return(o=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},i=r.createContext({onTabInteraction:function(e){},registerTab:function(e){},unregisterTab:function(e){}}),a=function(){return function(e){return function(t){return r.createElement(i.Consumer,null,function(n){return r.createElement(e,o({},t,{contextApi:n}))})}}}},function(e,t,n){\"use strict\";var r={};n.r(r),n.d(r,\"getTransformPropertyName\",function(){return ae});var o,i,a=n(0),l=n(13),u=n(1),s=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),c={ACTIVATED:\"mdc-select--activated\",DISABLED:\"mdc-select--disabled\",FOCUSED:\"mdc-select--focused\",INVALID:\"mdc-select--invalid\",OUTLINED:\"mdc-select--outlined\",REQUIRED:\"mdc-select--required\",ROOT:\"mdc-select\",SELECTED_ITEM_CLASS:\"mdc-list-item--selected\",WITH_LEADING_ICON:\"mdc-select--with-leading-icon\"},f={ARIA_CONTROLS:\"aria-controls\",ARIA_SELECTED_ATTR:\"aria-selected\",CHANGE_EVENT:\"MDCSelect:change\",ENHANCED_VALUE_ATTR:\"data-value\",HIDDEN_INPUT_SELECTOR:'input[type=\"hidden\"]',LABEL_SELECTOR:\".mdc-floating-label\",LEADING_ICON_SELECTOR:\".mdc-select__icon\",LINE_RIPPLE_SELECTOR:\".mdc-line-ripple\",MENU_SELECTOR:\".mdc-select__menu\",NATIVE_CONTROL_SELECTOR:\".mdc-select__native-control\",OUTLINE_SELECTOR:\".mdc-notched-outline\",SELECTED_ITEM_SELECTOR:\".\"+c.SELECTED_ITEM_CLASS,SELECTED_TEXT_SELECTOR:\".mdc-select__selected-text\"},d={LABEL_SCALE:.75},p=function(e){function t(n,r){void 0===r&&(r={});var o=e.call(this,u.a({},t.defaultAdapter,n))||this;return o.leadingIcon_=r.leadingIcon,o.helperText_=r.helperText,o}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return c},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return d},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return f},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},activateBottomLine:function(){},deactivateBottomLine:function(){},setValue:function(){},getValue:function(){return\"\"},floatLabel:function(){},getLabelWidth:function(){return 0},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){},openMenu:function(){},closeMenu:function(){},isMenuOpen:function(){return!1},setSelectedIndex:function(){},setDisabled:function(){},setRippleCenter:function(){},notifyChange:function(){},checkValidity:function(){return!1},setValid:function(){}}},enumerable:!0,configurable:!0}),t.prototype.setSelectedIndex=function(e){this.adapter_.setSelectedIndex(e),this.adapter_.closeMenu();this.handleChange(!0)},t.prototype.setValue=function(e){this.adapter_.setValue(e);this.handleChange(!0)},t.prototype.getValue=function(){return this.adapter_.getValue()},t.prototype.setDisabled=function(e){e?this.adapter_.addClass(c.DISABLED):this.adapter_.removeClass(c.DISABLED),this.adapter_.setDisabled(e),this.adapter_.closeMenu(),this.leadingIcon_&&this.leadingIcon_.setDisabled(e)},t.prototype.setHelperTextContent=function(e){this.helperText_&&this.helperText_.setContent(e)},t.prototype.layout=function(){var e=this.getValue().length>0;this.notchOutline(e)},t.prototype.handleMenuOpened=function(){this.adapter_.addClass(c.ACTIVATED)},t.prototype.handleMenuClosed=function(){this.adapter_.removeClass(c.ACTIVATED)},t.prototype.handleChange=function(e){void 0===e&&(e=!0);var t=this.getValue(),n=t.length>0,r=this.adapter_.hasClass(c.REQUIRED);this.notchOutline(n),this.adapter_.hasClass(c.FOCUSED)||this.adapter_.floatLabel(n),e&&(this.adapter_.notifyChange(t),r&&(this.setValid(this.isValid()),this.helperText_&&this.helperText_.setValidity(this.isValid())))},t.prototype.handleFocus=function(){this.adapter_.addClass(c.FOCUSED),this.adapter_.floatLabel(!0),this.notchOutline(!0),this.adapter_.activateBottomLine(),this.helperText_&&this.helperText_.showToScreenReader()},t.prototype.handleBlur=function(){this.adapter_.isMenuOpen()||(this.adapter_.removeClass(c.FOCUSED),this.handleChange(!1),this.adapter_.deactivateBottomLine(),this.adapter_.hasClass(c.REQUIRED)&&(this.setValid(this.isValid()),this.helperText_&&this.helperText_.setValidity(this.isValid())))},t.prototype.handleClick=function(e){this.adapter_.isMenuOpen()||(this.adapter_.setRippleCenter(e),this.adapter_.openMenu())},t.prototype.handleKeydown=function(e){if(!this.adapter_.isMenuOpen()){var t=\"Enter\"===e.key||13===e.keyCode,n=\"Space\"===e.key||32===e.keyCode,r=\"ArrowUp\"===e.key||38===e.keyCode,o=\"ArrowDown\"===e.key||40===e.keyCode;this.adapter_.hasClass(c.FOCUSED)&&(t||n||r||o)&&(this.adapter_.openMenu(),e.preventDefault())}},t.prototype.notchOutline=function(e){if(this.adapter_.hasOutline()){var t=this.adapter_.hasClass(c.FOCUSED);if(e){var n=d.LABEL_SCALE,r=this.adapter_.getLabelWidth()*n;this.adapter_.notchOutline(r)}else t||this.adapter_.closeOutline()}},t.prototype.setLeadingIconAriaLabel=function(e){this.leadingIcon_&&this.leadingIcon_.setAriaLabel(e)},t.prototype.setLeadingIconContent=function(e){this.leadingIcon_&&this.leadingIcon_.setContent(e)},t.prototype.setValid=function(e){this.adapter_.setValid(e)},t.prototype.isValid=function(){return this.adapter_.checkValidity()},t}(s),h={ICON_EVENT:\"MDCSelect:icon\",ICON_ROLE:\"button\"},m=[\"click\",\"keydown\"],y=function(e){function t(n){var r=e.call(this,u.a({},t.defaultAdapter,n))||this;return r.savedTabIndex_=null,r.interactionHandler_=function(e){return r.handleInteraction(e)},r}return u.b(t,e),Object.defineProperty(t,\"strings\",{get:function(){return h},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{getAttr:function(){return null},setAttr:function(){},removeAttr:function(){},setContent:function(){},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){},notifyIconAction:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){var e=this;this.savedTabIndex_=this.adapter_.getAttr(\"tabindex\"),m.forEach(function(t){e.adapter_.registerInteractionHandler(t,e.interactionHandler_)})},t.prototype.destroy=function(){var e=this;m.forEach(function(t){e.adapter_.deregisterInteractionHandler(t,e.interactionHandler_)})},t.prototype.setDisabled=function(e){this.savedTabIndex_&&(e?(this.adapter_.setAttr(\"tabindex\",\"-1\"),this.adapter_.removeAttr(\"role\")):(this.adapter_.setAttr(\"tabindex\",this.savedTabIndex_),this.adapter_.setAttr(\"role\",h.ICON_ROLE)))},t.prototype.setAriaLabel=function(e){this.adapter_.setAttr(\"aria-label\",e)},t.prototype.setContent=function(e){this.adapter_.setContent(e)},t.prototype.handleInteraction=function(e){var t=\"Enter\"===e.key||13===e.keyCode;(\"click\"===e.type||t)&&this.adapter_.notifyIconAction()},t}(s),v=n(6),g=n(100),b=n(36),_=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),E={LABEL_FLOAT_ABOVE:\"mdc-floating-label--float-above\",LABEL_SHAKE:\"mdc-floating-label--shake\",ROOT:\"mdc-floating-label\"},T=function(e){function t(n){var r=e.call(this,u.a({},t.defaultAdapter,n))||this;return r.shakeAnimationEndHandler_=function(){return r.handleShakeAnimationEnd_()},r}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return E},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},getWidth:function(){return 0},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){this.adapter_.registerInteractionHandler(\"animationend\",this.shakeAnimationEndHandler_)},t.prototype.destroy=function(){this.adapter_.deregisterInteractionHandler(\"animationend\",this.shakeAnimationEndHandler_)},t.prototype.getWidth=function(){return this.adapter_.getWidth()},t.prototype.shake=function(e){var n=t.cssClasses.LABEL_SHAKE;e?this.adapter_.addClass(n):this.adapter_.removeClass(n)},t.prototype.float=function(e){var n=t.cssClasses,r=n.LABEL_FLOAT_ABOVE,o=n.LABEL_SHAKE;e?this.adapter_.addClass(r):(this.adapter_.removeClass(r),this.adapter_.removeClass(o))},t.prototype.handleShakeAnimationEnd_=function(){var e=t.cssClasses.LABEL_SHAKE;this.adapter_.removeClass(e)},t}(_),C=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),O=function(){return(O=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},w=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},S=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.root=t.createElement(\"root\"),t}return C(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new T({addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},getWidth:function(){return e.root.ref?e.root.ref.scrollWidth:0},registerInteractionHandler:function(t,n){return e.root.addEventListener(t,n)},deregisterInteractionHandler:function(t,n){return e.root.removeEventListener(t,n)}})},t.prototype.sync=function(e,t){var n=this;this.syncProp(e.shake,t.shake,function(){n.foundation.shake(!!e.shake)}),this.syncProp(e.float,t.float,function(){n.foundation.float(!!e.float)})},t.prototype.getWidth=function(){return this.foundation.getWidth()},t.prototype.render=function(){var e=this.props,t=(e.shake,e.float,w(e,[\"shake\",\"float\"]));return a.createElement(\"label\",O({},this.root.props(O({},t,{className:\"mdc-floating-label\"})),{ref:this.root.setRef}))},t.displayName=\"FloatingLabel\",t}(b.a),x=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),I={LINE_RIPPLE_ACTIVE:\"mdc-line-ripple--active\",LINE_RIPPLE_DEACTIVATING:\"mdc-line-ripple--deactivating\"},A=function(e){function t(n){var r=e.call(this,u.a({},t.defaultAdapter,n))||this;return r.transitionEndHandler_=function(e){return r.handleTransitionEnd(e)},r}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return I},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setStyle:function(){},registerEventHandler:function(){},deregisterEventHandler:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){this.adapter_.registerEventHandler(\"transitionend\",this.transitionEndHandler_)},t.prototype.destroy=function(){this.adapter_.deregisterEventHandler(\"transitionend\",this.transitionEndHandler_)},t.prototype.activate=function(){this.adapter_.removeClass(I.LINE_RIPPLE_DEACTIVATING),this.adapter_.addClass(I.LINE_RIPPLE_ACTIVE)},t.prototype.setRippleCenter=function(e){this.adapter_.setStyle(\"transform-origin\",e+\"px center\")},t.prototype.deactivate=function(){this.adapter_.addClass(I.LINE_RIPPLE_DEACTIVATING)},t.prototype.handleTransitionEnd=function(e){var t=this.adapter_.hasClass(I.LINE_RIPPLE_DEACTIVATING);\"opacity\"===e.propertyName&&t&&(this.adapter_.removeClass(I.LINE_RIPPLE_ACTIVE),this.adapter_.removeClass(I.LINE_RIPPLE_DEACTIVATING))},t}(x),k=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),P=function(){return(P=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},N=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},L=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.root=t.createElement(\"root\"),t}return k(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new A({addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},hasClass:function(t){return e.root.hasClass(t)},setStyle:function(t,n){return e.root.setStyle(t,n)},registerEventHandler:function(t,n){return e.root.addEventListener(t,n)},deregisterEventHandler:function(t,n){return e.root.removeEventListener(t,n)}})},t.prototype.sync=function(e,t){var n=this;this.syncProp(e.active,t.active,function(){e.active?n.foundation.activate():n.foundation.deactivate()}),this.syncProp(e.center,t.center,function(){\"number\"===typeof e.center&&n.foundation.setRippleCenter(e.center)})},t.prototype.render=function(){var e=this.props,t=(e.active,e.center,N(e,[\"active\",\"center\"]));return a.createElement(\"div\",P({},this.root.props(P({},t,{className:\"mdc-line-ripple \"+(this.props.className||\"\")})),{ref:this.root.setRef}))},t.displayName=\"LineRipple\",t}(b.a),R=n(5),D=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),M={NOTCH_ELEMENT_SELECTOR:\".mdc-notched-outline__notch\"},j={NOTCH_ELEMENT_PADDING:8},F={NO_LABEL:\"mdc-notched-outline--no-label\",OUTLINE_NOTCHED:\"mdc-notched-outline--notched\",OUTLINE_UPGRADED:\"mdc-notched-outline--upgraded\"},U=function(e){function t(n){return e.call(this,u.a({},t.defaultAdapter,n))||this}return u.b(t,e),Object.defineProperty(t,\"strings\",{get:function(){return M},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"cssClasses\",{get:function(){return F},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return j},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},setNotchWidthProperty:function(){},removeNotchWidthProperty:function(){}}},enumerable:!0,configurable:!0}),t.prototype.notch=function(e){var n=t.cssClasses.OUTLINE_NOTCHED;e>0&&(e+=j.NOTCH_ELEMENT_PADDING),this.adapter_.setNotchWidthProperty(e),this.adapter_.addClass(n)},t.prototype.closeNotch=function(){var e=t.cssClasses.OUTLINE_NOTCHED;this.adapter_.removeClass(e),this.adapter_.removeNotchWidthProperty()},t}(D),B=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),V=function(){return(V=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},H=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},z=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return B(t,e),t.prototype.shouldComponentUpdate=function(){return!1},t.prototype.render=function(){return a.createElement(\"div\",{className:\"mdc-notched-outline__leading\"})},t}(a.Component),K=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return B(t,e),t.prototype.shouldComponentUpdate=function(){return!1},t.prototype.render=function(){return a.createElement(\"div\",{className:\"mdc-notched-outline__trailing\"})},t}(a.Component),W=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.root=t.createElement(\"root\"),t.notchElement=t.createElement(\"root\"),t.label=null,t}return B(t,e),t.prototype.componentDidMount=function(){var t=this;e.prototype.componentDidMount.call(this),this.label=this.root.ref&&this.root.ref.querySelector(\"label\"),this.label?(this.label.style.transitionDuration=\"0s\",this.root.addClass(U.cssClasses.OUTLINE_UPGRADED),requestAnimationFrame(function(){t.label&&(t.label.style.transitionDuration=\"\")})):this.root.addClass(U.cssClasses.NO_LABEL)},t.prototype.getDefaultFoundation=function(){var e=this;return new U({addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},setNotchWidthProperty:function(t){return e.notchElement.setStyle(\"width\",t+\"px\")},removeNotchWidthProperty:function(){return e.notchElement.setStyle(\"width\",\"\")}})},t.prototype.sync=function(e,t){var n=this;this.syncProp(e.notch,t.notch,function(){e.notch?n.foundation.notch(e.notch):n.foundation.closeNotch()})},t.prototype.render=function(){var e=this.props,t=e.children,n=H(e,[\"children\"]);return a.createElement(\"div\",V({},this.root.props(V({},n,{className:\"mdc-notched-outline\"})),{ref:this.root.setRef}),a.createElement(z,null),a.createElement(\"div\",V({},this.notchElement.props({}),{className:\"mdc-notched-outline__notch\",ref:this.notchElement.setRef}),t),a.createElement(K,null))},t.displayName=\"NotchedOutline\",t}(b.a),G=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),q={ANCHOR:\"mdc-menu-surface--anchor\",ANIMATING_CLOSED:\"mdc-menu-surface--animating-closed\",ANIMATING_OPEN:\"mdc-menu-surface--animating-open\",FIXED:\"mdc-menu-surface--fixed\",OPEN:\"mdc-menu-surface--open\",ROOT:\"mdc-menu-surface\"},X={CLOSED_EVENT:\"MDCMenuSurface:closed\",OPENED_EVENT:\"MDCMenuSurface:opened\",FOCUSABLE_ELEMENTS:[\"button:not(:disabled)\",'[href]:not([aria-disabled=\"true\"])',\"input:not(:disabled)\",\"select:not(:disabled)\",\"textarea:not(:disabled)\",'[tabindex]:not([tabindex=\"-1\"]):not([aria-disabled=\"true\"])'].join(\", \")},Y={TRANSITION_OPEN_DURATION:120,TRANSITION_CLOSE_DURATION:75,MARGIN_TO_EDGE:32,ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO:.67};!function(e){e[e.BOTTOM=1]=\"BOTTOM\",e[e.CENTER=2]=\"CENTER\",e[e.RIGHT=4]=\"RIGHT\",e[e.FLIP_RTL=8]=\"FLIP_RTL\"}(o||(o={})),function(e){e[e.TOP_LEFT=0]=\"TOP_LEFT\",e[e.TOP_RIGHT=4]=\"TOP_RIGHT\",e[e.BOTTOM_LEFT=1]=\"BOTTOM_LEFT\",e[e.BOTTOM_RIGHT=5]=\"BOTTOM_RIGHT\",e[e.TOP_START=8]=\"TOP_START\",e[e.TOP_END=12]=\"TOP_END\",e[e.BOTTOM_START=9]=\"BOTTOM_START\",e[e.BOTTOM_END=13]=\"BOTTOM_END\"}(i||(i={}));var Q,$=function(e){function t(n){var r=e.call(this,u.a({},t.defaultAdapter,n))||this;return r.isOpen_=!1,r.isQuickOpen_=!1,r.isHoistedElement_=!1,r.isFixedPosition_=!1,r.openAnimationEndTimerId_=0,r.closeAnimationEndTimerId_=0,r.animationRequestId_=0,r.anchorCorner_=i.TOP_START,r.anchorMargin_={top:0,right:0,bottom:0,left:0},r.position_={x:0,y:0},r}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return q},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return X},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return Y},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"Corner\",{get:function(){return i},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},hasAnchor:function(){return!1},isElementInContainer:function(){return!1},isFocused:function(){return!1},isRtl:function(){return!1},getInnerDimensions:function(){return{height:0,width:0}},getAnchorDimensions:function(){return null},getWindowDimensions:function(){return{height:0,width:0}},getBodyDimensions:function(){return{height:0,width:0}},getWindowScroll:function(){return{x:0,y:0}},setPosition:function(){},setMaxHeight:function(){},setTransformOrigin:function(){},saveFocus:function(){},restoreFocus:function(){},notifyClose:function(){},notifyOpen:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){var e=t.cssClasses,n=e.ROOT,r=e.OPEN;if(!this.adapter_.hasClass(n))throw new Error(n+\" class required in root element.\");this.adapter_.hasClass(r)&&(this.isOpen_=!0)},t.prototype.destroy=function(){clearTimeout(this.openAnimationEndTimerId_),clearTimeout(this.closeAnimationEndTimerId_),cancelAnimationFrame(this.animationRequestId_)},t.prototype.setAnchorCorner=function(e){this.anchorCorner_=e},t.prototype.setAnchorMargin=function(e){this.anchorMargin_.top=e.top||0,this.anchorMargin_.right=e.right||0,this.anchorMargin_.bottom=e.bottom||0,this.anchorMargin_.left=e.left||0},t.prototype.setIsHoisted=function(e){this.isHoistedElement_=e},t.prototype.setFixedPosition=function(e){this.isFixedPosition_=e},t.prototype.setAbsolutePosition=function(e,t){this.position_.x=this.isFinite_(e)?e:0,this.position_.y=this.isFinite_(t)?t:0},t.prototype.setQuickOpen=function(e){this.isQuickOpen_=e},t.prototype.isOpen=function(){return this.isOpen_},t.prototype.open=function(){var e=this;this.adapter_.saveFocus(),this.isQuickOpen_||this.adapter_.addClass(t.cssClasses.ANIMATING_OPEN),this.animationRequestId_=requestAnimationFrame(function(){e.adapter_.addClass(t.cssClasses.OPEN),e.dimensions_=e.adapter_.getInnerDimensions(),e.autoPosition_(),e.isQuickOpen_?e.adapter_.notifyOpen():e.openAnimationEndTimerId_=setTimeout(function(){e.openAnimationEndTimerId_=0,e.adapter_.removeClass(t.cssClasses.ANIMATING_OPEN),e.adapter_.notifyOpen()},Y.TRANSITION_OPEN_DURATION)}),this.isOpen_=!0},t.prototype.close=function(e){var n=this;void 0===e&&(e=!1),this.isQuickOpen_||this.adapter_.addClass(t.cssClasses.ANIMATING_CLOSED),requestAnimationFrame(function(){n.adapter_.removeClass(t.cssClasses.OPEN),n.isQuickOpen_?n.adapter_.notifyClose():n.closeAnimationEndTimerId_=setTimeout(function(){n.closeAnimationEndTimerId_=0,n.adapter_.removeClass(t.cssClasses.ANIMATING_CLOSED),n.adapter_.notifyClose()},Y.TRANSITION_CLOSE_DURATION)}),this.isOpen_=!1,e||this.maybeRestoreFocus_()},t.prototype.handleBodyClick=function(e){var t=e.target;this.adapter_.isElementInContainer(t)||this.close()},t.prototype.handleKeydown=function(e){var t=e.keyCode;(\"Escape\"===e.key||27===t)&&this.close()},t.prototype.autoPosition_=function(){var e;this.measurements_=this.getAutoLayoutMeasurements_();var t=this.getOriginCorner_(),n=this.getMenuSurfaceMaxHeight_(t),r=this.hasBit_(t,o.BOTTOM)?\"bottom\":\"top\",i=this.hasBit_(t,o.RIGHT)?\"right\":\"left\",a=this.getHorizontalOriginOffset_(t),l=this.getVerticalOriginOffset_(t),u=this.measurements_,s=u.anchorSize,c=u.surfaceSize,f=((e={})[i]=a,e[r]=l,e);s.width/c.width>Y.ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO&&(i=\"center\"),(this.isHoistedElement_||this.isFixedPosition_)&&this.adjustPositionForHoistedElement_(f),this.adapter_.setTransformOrigin(i+\" \"+r),this.adapter_.setPosition(f),this.adapter_.setMaxHeight(n?n+\"px\":\"\")},t.prototype.getAutoLayoutMeasurements_=function(){var e=this.adapter_.getAnchorDimensions(),t=this.adapter_.getBodyDimensions(),n=this.adapter_.getWindowDimensions(),r=this.adapter_.getWindowScroll();return e||(e={top:this.position_.y,right:this.position_.x,bottom:this.position_.y,left:this.position_.x,width:0,height:0}),{anchorSize:e,bodySize:t,surfaceSize:this.dimensions_,viewportDistance:{top:e.top,right:n.width-e.right,bottom:n.height-e.bottom,left:e.left},viewportSize:n,windowScroll:r}},t.prototype.getOriginCorner_=function(){var e=i.TOP_LEFT,t=this.measurements_,n=t.viewportDistance,r=t.anchorSize,a=t.surfaceSize,l=this.hasBit_(this.anchorCorner_,o.BOTTOM),u=l?n.top+r.height+this.anchorMargin_.bottom:n.top+this.anchorMargin_.top,s=l?n.bottom-this.anchorMargin_.bottom:n.bottom+r.height-this.anchorMargin_.top,c=a.height-u,f=a.height-s;f>0&&c<f&&(e=this.setBit_(e,o.BOTTOM));var d=this.adapter_.isRtl(),p=this.hasBit_(this.anchorCorner_,o.FLIP_RTL),h=this.hasBit_(this.anchorCorner_,o.RIGHT),m=h&&!d||!h&&p&&d,y=m?n.left+r.width+this.anchorMargin_.right:n.left+this.anchorMargin_.left,v=m?n.right-this.anchorMargin_.right:n.right+r.width-this.anchorMargin_.left,g=a.width-y,b=a.width-v;return(g<0&&m&&d||h&&!m&&g<0||b>0&&g<b)&&(e=this.setBit_(e,o.RIGHT)),e},t.prototype.getMenuSurfaceMaxHeight_=function(e){var n=this.measurements_.viewportDistance,r=0,i=this.hasBit_(e,o.BOTTOM),a=this.hasBit_(this.anchorCorner_,o.BOTTOM),l=t.numbers.MARGIN_TO_EDGE;return i?(r=n.top+this.anchorMargin_.top-l,a||(r+=this.measurements_.anchorSize.height)):(r=n.bottom-this.anchorMargin_.bottom+this.measurements_.anchorSize.height-l,a&&(r-=this.measurements_.anchorSize.height)),r},t.prototype.getHorizontalOriginOffset_=function(e){var t=this.measurements_.anchorSize,n=this.hasBit_(e,o.RIGHT),r=this.hasBit_(this.anchorCorner_,o.RIGHT);if(n){var i=r?t.width-this.anchorMargin_.left:this.anchorMargin_.right;return this.isHoistedElement_||this.isFixedPosition_?i-(this.measurements_.viewportSize.width-this.measurements_.bodySize.width):i}return r?t.width-this.anchorMargin_.right:this.anchorMargin_.left},t.prototype.getVerticalOriginOffset_=function(e){var t=this.measurements_.anchorSize,n=this.hasBit_(e,o.BOTTOM),r=this.hasBit_(this.anchorCorner_,o.BOTTOM);return n?r?t.height-this.anchorMargin_.top:-this.anchorMargin_.bottom:r?t.height+this.anchorMargin_.bottom:this.anchorMargin_.top},t.prototype.adjustPositionForHoistedElement_=function(e){var t,n,r=this.measurements_,o=r.windowScroll,i=r.viewportDistance,a=Object.keys(e);try{for(var l=u.d(a),s=l.next();!s.done;s=l.next()){var c=s.value,f=e[c]||0;f+=i[c],this.isFixedPosition_||(\"top\"===c?f+=o.y:\"bottom\"===c?f-=o.y:\"left\"===c?f+=o.x:f-=o.x),e[c]=f}}catch(d){t={error:d}}finally{try{s&&!s.done&&(n=l.return)&&n.call(l)}finally{if(t)throw t.error}}},t.prototype.maybeRestoreFocus_=function(){var e=this.adapter_.isFocused(),t=document.activeElement&&this.adapter_.isElementInContainer(document.activeElement);(e||t)&&this.adapter_.restoreFocus()},t.prototype.hasBit_=function(e,t){return Boolean(e&t)},t.prototype.setBit_=function(e,t){return e|t},t.prototype.isFinite_=function(e){return\"number\"===typeof e&&isFinite(e)},t}(G),Z={MENU_SELECTED_LIST_ITEM:\"mdc-menu-item--selected\",MENU_SELECTION_GROUP:\"mdc-menu__selection-group\",ROOT:\"mdc-menu\"},J={ARIA_CHECKED_ATTR:\"aria-checked\",CHECKBOX_SELECTOR:'input[type=\"checkbox\"]',LIST_SELECTOR:\".mdc-list\",SELECTED_EVENT:\"MDCMenu:selected\"},ee={FOCUS_ROOT_INDEX:-1};!function(e){e[e.NONE=0]=\"NONE\",e[e.LIST_ROOT=1]=\"LIST_ROOT\",e[e.FIRST_ITEM=2]=\"FIRST_ITEM\",e[e.LAST_ITEM=3]=\"LAST_ITEM\"}(Q||(Q={}));var te,ne=function(e){function t(n){var r=e.call(this,u.a({},t.defaultAdapter,n))||this;return r.closeAnimationEndTimerId_=0,r.defaultFocusState_=Q.LIST_ROOT,r}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return Z},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return J},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return ee},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClassToElementAtIndex:function(){},removeClassFromElementAtIndex:function(){},addAttributeToElementAtIndex:function(){},removeAttributeFromElementAtIndex:function(){},elementContainsClass:function(){return!1},closeSurface:function(){},getElementIndex:function(){return-1},notifySelected:function(){},getMenuItemCount:function(){return 0},focusItemAtIndex:function(){},focusListRoot:function(){},getSelectedSiblingOfItemAtIndex:function(){return-1},isSelectableItemAtIndex:function(){return!1}}},enumerable:!0,configurable:!0}),t.prototype.destroy=function(){this.closeAnimationEndTimerId_&&clearTimeout(this.closeAnimationEndTimerId_),this.adapter_.closeSurface()},t.prototype.handleKeydown=function(e){var t=e.key,n=e.keyCode;(\"Tab\"===t||9===n)&&this.adapter_.closeSurface(!0)},t.prototype.handleItemAction=function(e){var t=this,n=this.adapter_.getElementIndex(e);n<0||(this.adapter_.notifySelected({index:n}),this.adapter_.closeSurface(),this.closeAnimationEndTimerId_=setTimeout(function(){t.adapter_.isSelectableItemAtIndex(n)&&t.setSelectedIndex(n)},$.numbers.TRANSITION_CLOSE_DURATION))},t.prototype.handleMenuSurfaceOpened=function(){switch(this.defaultFocusState_){case Q.FIRST_ITEM:this.adapter_.focusItemAtIndex(0);break;case Q.LAST_ITEM:this.adapter_.focusItemAtIndex(this.adapter_.getMenuItemCount()-1);break;case Q.NONE:break;default:this.adapter_.focusListRoot()}},t.prototype.setDefaultFocusState=function(e){this.defaultFocusState_=e},t.prototype.setSelectedIndex=function(e){if(this.validatedIndex_(e),!this.adapter_.isSelectableItemAtIndex(e))throw new Error(\"MDCMenuFoundation: No selection group at specified index.\");var t=this.adapter_.getSelectedSiblingOfItemAtIndex(e);t>=0&&(this.adapter_.removeAttributeFromElementAtIndex(t,J.ARIA_CHECKED_ATTR),this.adapter_.removeClassFromElementAtIndex(t,Z.MENU_SELECTED_LIST_ITEM)),this.adapter_.addClassToElementAtIndex(e,Z.MENU_SELECTED_LIST_ITEM),this.adapter_.addAttributeToElementAtIndex(e,J.ARIA_CHECKED_ATTR,\"true\")},t.prototype.validatedIndex_=function(e){var t=this.adapter_.getMenuItemCount();if(!(e>=0&&e<t))throw new Error(\"MDCMenuFoundation: No list item at specified index.\")},t}(G),re=n(103),oe=n(99),ie=n(98);function ae(e,t){if(void 0===t&&(t=!1),void 0===te||t){var n=e.document.createElement(\"div\");te=\"transform\"in n.style?\"transform\":\"webkitTransform\"}return te}var le=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),ue=function(){return(ue=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},se=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},ce={bottomEnd:\"BOTTOM_END\",bottomLeft:\"BOTTOM_LEFT\",bottomRight:\"BOTTOM_RIGHT\",bottomStart:\"BOTTOM_START\",topEnd:\"TOP_END\",topLeft:\"TOP_LEFT\",topRight:\"TOP_RIGHT\",topStart:\"TOP_START\"},fe=Object(v.a)({displayName:\"MenuSurfaceRoot\",classNames:function(e){return[\"mdc-menu-surface\",{\"mdc-menu-surface--fixed\":e.fixed}]},consumeProps:[\"fixed\"]}),de=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.anchorElement=null,n.previousFocus=null,n.firstFocusableElement=null,n.lastFocusableElement=null,n.hoisted=!1,n.handleKeydown=n.handleKeydown.bind(n),n.handleBodyClick=n.handleBodyClick.bind(n),n}return le(t,e),t.prototype.componentDidMount=function(){if(this.root.ref){var t=Object(ie.a)(this.root.ref,\".\"+$.cssClasses.ANCHOR);t&&(this.anchorElement=t)}e.prototype.componentDidMount.call(this)},t.prototype.componentWillUnmount=function(){this.hoisted&&this.unhoistMenuFromBody(),e.prototype.componentWillUnmount.call(this)},Object.defineProperty(t.prototype,\"open\",{get:function(){return this.foundation.isOpen()},set:function(e){if(e&&this.foundation&&!this.foundation.isOpen()){var t=this.root.ref?this.root.ref.querySelectorAll($.strings.FOCUSABLE_ELEMENTS):[];this.firstFocusableElement=t.length>0?t[0]:null,this.lastFocusableElement=t.length>0?t[t.length-1]:null,this.foundation.open()}else this.foundation&&this.foundation.isOpen()&&this.foundation.close()},enumerable:!0,configurable:!0}),t.prototype.getDefaultFoundation=function(){var e=this;return new $(ue({addClass:function(t){e.root.addClass(t)},removeClass:function(t){e.root.removeClass(t)},hasClass:function(t){return\"mdc-menu-surface\"===t||e.root.hasClass(t)},hasAnchor:function(){return!!e.anchorElement},notifyClose:function(){e.emit(\"onClose\",{}),e.deregisterBodyClickListener(),e.props.open&&(e.open=e.props.open)},notifyOpen:function(){e.emit(\"onOpen\",{}),e.registerBodyClickListener()},isElementInContainer:function(t){return e.root.ref===t||!!e.root.ref&&e.root.ref.contains(t)},isRtl:function(){return!!e.root.ref&&\"rtl\"===getComputedStyle(e.root.ref).getPropertyValue(\"direction\")},setTransformOrigin:function(t){e.root.setStyle(r.getTransformPropertyName(window)+\"-origin\",t)}},this.getFocusAdapterMethods(),this.getDimensionAdapterMethods()))},t.prototype.getFocusAdapterMethods=function(){var e=this;return{isFocused:function(){return document.activeElement===e.root.ref},saveFocus:function(){e.previousFocus=document.activeElement},restoreFocus:function(){e.root.ref&&e.root.ref.contains(document.activeElement)&&e.previousFocus&&e.previousFocus.focus&&e.previousFocus.focus()},isFirstElementFocused:function(){return!!e.firstFocusableElement&&e.firstFocusableElement===document.activeElement},isLastElementFocused:function(){return!!e.firstFocusableElement&&e.firstFocusableElement===document.activeElement},focusFirstElement:function(){return!!e.firstFocusableElement&&e.firstFocusableElement.focus&&e.firstFocusableElement.focus()},focusLastElement:function(){return!!e.firstFocusableElement&&e.firstFocusableElement.focus&&e.firstFocusableElement.focus()}}},t.prototype.getDimensionAdapterMethods=function(){var e=this;return{getInnerDimensions:function(){return{width:e.root.ref?e.root.ref.offsetWidth:0,height:e.root.ref?e.root.ref.offsetHeight:0}},getAnchorDimensions:function(){return e.anchorElement&&e.anchorElement.getBoundingClientRect()},getWindowDimensions:function(){return{width:window.innerWidth,height:window.innerHeight}},getBodyDimensions:function(){return{width:document.body.clientWidth,height:document.body.clientHeight}},getWindowScroll:function(){return{x:window.pageXOffset,y:window.pageYOffset}},setPosition:function(t){e.root.setStyle(\"left\",void 0!==t.left?t.left:null),e.root.setStyle(\"right\",void 0!==t.right?t.right:null),e.root.setStyle(\"top\",void 0!==t.top?t.top:null),e.root.setStyle(\"bottom\",void 0!==t.bottom?t.bottom:null)},setMaxHeight:function(t){e.root.setStyle(\"maxHeight\",t)}}},t.prototype.sync=function(e,t){var n=this;this.syncProp(e.fixed,t.fixed,function(){n.foundation.setFixedPosition(!!e.fixed)}),this.syncProp(e.hoistToBody,t.hoistToBody,function(){e.hoistToBody?n.hoistMenuToBody():n.unhoistMenuFromBody()});var r=e.anchorCorner&&function(e){return $.Corner[ce[e]]}(e.anchorCorner);this.syncProp(r,this.foundation.anchorCorner_,function(){r&&(n.foundation.setAnchorCorner(r),n.foundation.dimensions_=n.foundation.adapter_.getInnerDimensions(),n.foundation.autoPosition_())}),this.syncProp(e.open,t.open,function(){n.open=!!e.open})},t.prototype.hoistMenuToBody=function(){var e=this;this.root.ref&&this.root.ref.parentElement&&(document.body.appendChild(this.root.ref.parentElement.removeChild(this.root.ref)),this.hoisted=!0,this.foundation.setIsHoisted(!0),this.props.open&&setTimeout(function(){return e.foundation.autoPosition_()}))},t.prototype.unhoistMenuFromBody=function(){this.anchorElement&&this.root.ref&&(this.anchorElement.appendChild(this.root.ref),this.hoisted=!1,this.foundation.setIsHoisted(!1))},t.prototype.setAnchorCorner=function(e){this.foundation.setAnchorCorner(e)},t.prototype.registerBodyClickListener=function(){document.body.addEventListener(\"click\",this.handleBodyClick),document.body.addEventListener(\"touchstart\",this.handleBodyClick)},t.prototype.deregisterBodyClickListener=function(){document.body.removeEventListener(\"click\",this.handleBodyClick),document.body.removeEventListener(\"touchstart\",this.handleBodyClick)},t.prototype.handleBodyClick=function(e){this.foundation&&this.foundation.handleBodyClick(e)},t.prototype.handleKeydown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.foundation.handleKeydown(e)},t.prototype.render=function(){var e=this.props,t=e.children,n=(e.open,e.anchorCorner,e.onOpen,e.onClose,e.hoistToBody,se(e,[\"children\",\"open\",\"anchorCorner\",\"onOpen\",\"onClose\",\"hoistToBody\"]));return a.createElement(fe,ue({},this.root.props(n),{ref:this.root.setRef,onKeyDown:this.handleKeydown}),t)},t.displayName=\"MenuSurface\",t}(b.a),pe=Object(v.a)({displayName:\"MenuSurfaceAnchor\",classNames:[\"mdc-menu-surface--anchor\"]}),he=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),me=function(){return(me=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},ye=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},ve=Object(v.a)({displayName:\"MenuItems\",tag:re.a,classNames:[\"mdc-list mdc-menu__items\"],defaultProps:{role:\"menu\"}}),ge=Object(v.a)({displayName:\"MenuItem\",tag:oe.d,defaultProps:{role:\"menuitem\",tabIndex:0}}),be=function(e){function t(t){var n=e.call(this,t)||this;return n.list=null,n.menuSurface=null,n.handleKeydown=n.handleKeydown.bind(n),n.handleClick=n.handleClick.bind(n),n.handleOpen=n.handleOpen.bind(n),n}return he(t,e),Object.defineProperty(t.prototype,\"items\",{get:function(){return this.list?this.list.listElements:[]},enumerable:!0,configurable:!0}),t.prototype.hoistMenuToBody=function(){this.menuSurface&&this.menuSurface.hoistMenuToBody()},t.prototype.setAnchorCorner=function(e){this.menuSurface&&this.menuSurface.setAnchorCorner(e)},t.prototype.setAnchorElement=function(e){this.menuSurface&&(this.menuSurface.anchorElement=e)},t.prototype.getDefaultFoundation=function(){var e=this;return new ne({addClassToElementAtIndex:function(t,n){e.items[t].classList.add(n)},removeClassFromElementAtIndex:function(t,n){e.items[t].classList.remove(n)},addAttributeToElementAtIndex:function(t,n,r){e.items[t].setAttribute(n,r)},removeAttributeFromElementAtIndex:function(t,n){e.items[t].removeAttribute(n)},elementContainsClass:function(e,t){return e.classList.contains(t)},closeSurface:function(){e.menuSurface&&(e.menuSurface.open=!1)},getElementIndex:function(t){return e.items.indexOf(t)},notifySelected:function(t){return e.emit(\"onSelect\",{index:t.index,item:e.items[t.index]})},getMenuItemCount:function(){return e.items.length},focusItemAtIndex:function(t){return e.items[t].focus()},focusListRoot:function(){return e.list&&e.list.root&&e.list.root.ref&&e.list.root.ref.focus()}})},t.prototype.handleClick=function(e){this.props.onClick&&this.props.onClick(e);var t=Object(ie.a)(e.target,\".mdc-list-item\");t&&this.foundation.handleItemAction(t)},t.prototype.handleKeydown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.foundation.handleKeydown(e),13===e.which&&e.target instanceof Element&&e.target.classList.contains(re.a.cssClasses.LIST_ITEM_CLASS)&&this.foundation.handleItemAction(e.target)},t.prototype.handleOpen=function(e){var t=this.items;this.props.focusOnOpen&&t.length>0&&!t.some(function(e){return e===document.activeElement})&&t[0].focus(),this.props.onOpen&&this.props.onOpen(e)},t.prototype.render=function(){var e=this,t=this.props,n=t.children,r=(t.focusOnOpen,ye(t,[\"children\",\"focusOnOpen\"])),o=!(n&&\"object\"===typeof n&&\"MenuItems\"===(n.type||{}).displayName);return a.createElement(de,me({},r,{\"aria-hidden\":!r.open,className:\"mdc-menu \"+(r.className||\"\"),onKeyDown:this.handleKeydown,onClick:this.handleClick,onOpen:this.handleOpen,ref:function(t){return e.menuSurface=t}}),o?a.createElement(ve,{ref:function(t){return e.list=t}},n):a.cloneElement(n,{ref:function(t){return e.list=t}}))},t.displayName=\"Menu\",t.defaultProps={focusOnOpen:!0},t}(b.a),_e=function(e){var t;return(t=function(t){function n(){var e=null!==t&&t.apply(this,arguments)||this;return e.state={open:!!e.props.open},e}return he(n,t),n.prototype.componentDidMount=function(){this.syncWithOpenProp(this.props.open)},n.prototype.componentDidUpdate=function(e){this.syncWithOpenProp(e.open)},n.prototype.syncWithOpenProp=function(e){void 0!==e&&this.state.open!==e&&this.setState({open:e})},n.prototype.render=function(){var t=this,n=this.props,r=n.handle,o=n.onClose,i=n.children,l=n.rootProps,u=void 0===l?{}:l,s=n.open,c=ye(n,[\"handle\",\"onClose\",\"children\",\"rootProps\",\"open\"]),f=a.cloneElement(r,me({},r.props,{onClick:function(e){t.setState({open:!t.state.open}),r.props.onClick&&r.props.onClick(e)}}));return a.createElement(pe,me({},u),a.createElement(e,me({},c,{onClose:function(e){t.setState({open:!!s||!1}),o&&o(e)},open:this.state.open}),i),f)},n}(a.Component)).displayName=\"Simple\"+e.displayName,t},Ee=(_e(be),_e(de),n(7));n.d(t,\"a\",function(){return Ne});var Te=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),Ce=function(){return(Ce=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},Oe=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},we=Object(Ee.b)()(Object(v.a)({displayName:\"SelectRoot\",defaultProps:{role:\"listbox\"},classNames:function(e){return[\"mdc-select\",{\"mdc-select--outlined\":!!e.outlined,\"mdc-select--required\":!!e.required,\"mdc-select--invalid\":!!e.invalid,\"mdc-select--with-leading-icon\":!!e.icon}]},consumeProps:[\"outlined\",\"icon\",\"required\",\"invalid\"]})),Se=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Te(t,e),t.prototype.shouldComponentUpdate=function(){return!1},t.prototype.render=function(){return a.createElement(\"i\",{className:\"mdc-select__dropdown-icon\"})},t}(a.Component),xe=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Te(t,e),t.prototype.render=function(){var e=this.props,t=e.selectOptions,n=e.placeholder,r=void 0===n?\"\":n,o=e.children,i=e.elementRef,l=Oe(e,[\"selectOptions\",\"placeholder\",\"children\",\"elementRef\"]);return a.createElement(\"select\",Ce({tabIndex:0},l,{ref:i,className:\"mdc-select__native-control \"+(l.className||\"\")}),!this.props.value&&!this.props.defaultValue&&a.createElement(\"option\",{value:\"\",disabled:\"\"===r},r),!!t&&t.map(function(e,t){var n=e.label,r=e.options,o=Oe(e,[\"label\",\"options\"]);return r?a.createElement(\"optgroup\",{label:n,key:n},r.map(function(e,t){var n=e.label,r=Oe(e,[\"label\"]);return a.createElement(\"option\",Ce({key:n+\"-\"+r.value},r,{value:r.value}),n)})):a.createElement(\"option\",Ce({key:n+\"-\"+o.value},o,{value:o.value}),n)}),o)},t.displayName=\"SelectNativeControl\",t}(a.Component),Ie=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Te(t,e),t.prototype.render=function(){var e=this.props,t=e.selectOptions,n=e.apiRef,r=e.selectedIndex,o=e.placeholder,i=e.children,l=Oe(e,[\"selectOptions\",\"apiRef\",\"selectedIndex\",\"placeholder\",\"children\"]),u=0,s=(void 0!==o||void 0===this.props.value&&void 0===this.props.defaultValue)&&0===u++;return a.createElement(be,Ce({},l,{ref:n,className:\"mdc-select__menu\"}),s&&a.createElement(ge,{selected:u-1===r,\"data-value\":\"\"},o),t.map(function(e,n){var o=e.label,i=e.options,l=Oe(e,[\"label\",\"options\"]);return i?a.createElement(oe.b,{key:o},a.createElement(oe.c,{theme:\"textDisabledOnBackground\"},o),a.createElement(ve,null,i.map(function(e,t){var n=e.label,o=Oe(e,[\"label\"]);return u+=1,a.createElement(ge,Ce({key:n+\"-\"+o.value,activated:u-1===r},o,{\"data-value\":o.value}),n)})),n<t.length-1&&a.createElement(oe.a,null)):(u+=1,a.createElement(ge,Ce({key:o+\"-\"+l.value,activated:u-1===r},l,{\"data-value\":l.value}),o))}),i)},t}(a.Component),Ae=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.lineRipple=n.createElement(\"lineRipple\"),n.outline=n.createElement(\"outline\"),n.label=n.createElement(\"label\"),n.id=n.props.id||Object(g.a)(\"select\"),n.nativeControl=null,n.selectedText=null,n.menuElement=null,n.menu=null,n.hiddenInput_=null,n.leadingIcon_=null,n.trailingIcon_=null,n.state={selectedIndex:void 0!==n.props.placeholder?0:-1,menuOpen:!1,selectedTextContent:\"\"},n.handleChange=n.handleChange.bind(n),n.handleFocus=n.handleFocus.bind(n),n.handleBlur=n.handleBlur.bind(n),n.handleClick=n.handleClick.bind(n),n.handleKeydown=n.handleKeydown.bind(n),n.handleMenuSelected=n.handleMenuSelected.bind(n),n.handleMenuOpened=n.handleMenuOpened.bind(n),n.handleMenuClosed=n.handleMenuClosed.bind(n),n}return Te(t,e),t.prototype.componentDidMount=function(){e.prototype.componentDidMount.call(this);var t=this.props,n=t.enhanced,r=t.value;this.menu&&(this.menuElement=this.root.ref&&this.root.ref.querySelector(\".mdc-select__menu\"),this.menu.hoistMenuToBody(),this.root.ref&&this.menu.setAnchorElement(this.root.ref),this.hiddenInput_&&this.hiddenInput_.value?this.getEnhancedSelectAdapterMethods_().setValue(this.hiddenInput_?this.hiddenInput_.value:\"\"):n&&this.getEnhancedSelectAdapterMethods_().setValue(r||\"\"));this.foundation.handleChange(!1),this.props.disabled&&this.foundation.setDisabled(!0),this.nativeControl&&document.activeElement===this.nativeControl&&this.foundation.handleFocus()},t.prototype.getDefaultFoundation=function(){return new p(Ce({},this.props.enhanced?this.getEnhancedSelectAdapterMethods_():this.getNativeSelectAdapterMethods_(),this.getCommonAdapterMethods_(),this.getOutlineAdapterMethods_(),this.getLabelAdapterMethods_()),this.getFoundationMap_())},t.prototype.getNativeSelectAdapterMethods_=function(){var e=this;return{getValue:function(){var t=e.nativeControl&&e.nativeControl.value;return\"\"===t&&e.props.placeholder?\" \":t||\"\"},setValue:function(t){return e.nativeControl&&(e.nativeControl.value=t)},openMenu:function(){},closeMenu:function(){},isMenuOpen:function(){return!1},setSelectedIndex:function(t){e.nativeControl&&(e.nativeControl.selectedIndex=t)},setDisabled:function(t){return e.nativeControl&&(e.nativeControl.disabled=t)},setValid:function(t){t?e.root.removeClass(p.cssClasses.INVALID):e.root.addClass(p.cssClasses.INVALID)},checkValidity:function(){return!!e.nativeControl&&e.nativeControl.checkValidity()}}},t.prototype.getEnhancedSelectAdapterMethods_=function(){var e=this;return{getValue:function(){var t=\"\",n=e.menuElement&&e.menuElement.querySelector(\".mdc-list-item--activated\");return n&&n.hasAttribute(p.strings.ENHANCED_VALUE_ATTR)&&(t=n.getAttribute(p.strings.ENHANCED_VALUE_ATTR)),\"\"===t&&e.props.placeholder?\" \":t},setValue:function(t){if(e.menuElement){var n=e.menuElement.querySelector(\"[\"+p.strings.ENHANCED_VALUE_ATTR+'=\"'+t+'\"]'),r=n&&e.menu?e.menu.items.indexOf(n):-1,o=e.menu&&e.menu.items[r],i=o&&o.textContent?o.textContent.trim():\"\";e.setState({selectedIndex:r,selectedTextContent:i},function(){e.foundation.layout(),e.foundation.adapter_.floatLabel(!!i)})}},openMenu:function(){e.setState({menuOpen:!0})},closeMenu:function(){e.setState({menuOpen:!1})},isMenuOpen:function(){return e.state.menuOpen},setSelectedIndex:function(t){e.setState({selectedIndex:t})},setDisabled:function(e){},checkValidity:function(){var t=e.root.ref&&e.root.ref.classList;return!(t&&t.contains(p.cssClasses.REQUIRED)&&!t.contains(p.cssClasses.DISABLED))||-1!==e.state.selectedIndex&&(0!==e.state.selectedIndex||!!e.value)},setValid:function(t){e.selectedText&&e.selectedText.setAttribute(\"aria-invalid\",(!t).toString()),t?e.root.removeClass(p.cssClasses.INVALID):e.root.addClass(p.cssClasses.INVALID)}}},t.prototype.getCommonAdapterMethods_=function(){var e=this;return{addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},hasClass:function(t){return e.root.hasClass(t)},isRtl:function(){return e.root.ref&&\"rtl\"===window.getComputedStyle(e.root.ref).getPropertyValue(\"direction\")},setRippleCenter:function(t){return e.lineRipple.setProp(\"center\",t)},activateBottomLine:function(){return e.lineRipple.setProp(\"active\",!0)},deactivateBottomLine:function(){return e.lineRipple.setProp(\"active\",!1)},notifyChange:function(e){}}},t.prototype.getOutlineAdapterMethods_=function(){var e=this;return{hasOutline:function(){return!!e.props.outlined},notchOutline:function(t){e.outline.setProp(\"notch\",t)},closeOutline:function(){e.outline.removeProp(\"notch\")}}},t.prototype.getLabelAdapterMethods_=function(){var e=this;return{floatLabel:function(t){e.label.setProp(\"float\",t)},getLabelWidth:function(){return e.label.ref?e.label.ref.getWidth():0}}},t.prototype.getFoundationMap_=function(){return{leadingIcon:this.leadingIcon_&&this.leadingIcon_.foundation||void 0}},t.prototype.sync=function(e,t){e.value===t.value&&e.options===t.options&&JSON.stringify(e.options)===JSON.stringify(t.options)||this.foundation.setValue(e.value||\"\"),void 0===e.disabled||t&&t.disabled===e.disabled||this.foundation.setDisabled(e.disabled)},Object.defineProperty(t.prototype,\"value\",{get:function(){return this.foundation.getValue()},set:function(e){this.foundation.setValue(e)},enumerable:!0,configurable:!0}),t.prototype.handleChange=function(e){this.props.onChange&&this.props.onChange(e),this.foundation.handleChange(!0)},t.prototype.handleFocus=function(e){this.props.onFocus&&this.props.onFocus(e),this.foundation&&this.foundation.handleFocus()},t.prototype.handleBlur=function(e){this.props.onBlur&&this.props.onBlur(e),this.foundation.handleBlur()},t.prototype.handleClick=function(e){var t=this,n=this.props,r=n.onMouseDown,o=n.onTouchStart;\"mousedown\"===e.type&&r&&r(e),\"touchstart\"===e.type&&o&&o(e);this.selectedText&&this.selectedText.focus();var i=function(e){var t=e.target.getBoundingClientRect();return e.clientX-t.left}(e);setTimeout(function(){t.foundation.handleClick(i)})},t.prototype.handleKeydown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.foundation.handleKeydown(e)},t.prototype.handleMenuSelected=function(e){var t=e.detail.item.dataset.value;this.emit(\"onChange\",{index:e.detail.index,value:t},!0),void 0===this.props.value&&this.getEnhancedSelectAdapterMethods_().setValue(t||\"\")},t.prototype.handleMenuOpened=function(){this.menu&&this.state.selectedIndex>=0&&this.menu.items[this.state.selectedIndex].focus()},t.prototype.handleMenuClosed=function(){this.setState({menuOpen:!1}),document.activeElement!==this.selectedText&&this.foundation.handleBlur()},t.prototype.renderIcon=function(e,t){var n=this;return e&&\"string\"===typeof e||e.type&&e.type.displayName!==ke.displayName?a.createElement(ke,{ref:function(e){\"leadingIcon_\"===t?n.leadingIcon_=e&&e.foundation:n.trailingIcon_=e&&e.foundation},tabIndex:\"trailingIcon_\"===t?0:void 0,icon:e}):e},t.prototype.renderHelpText=function(){var e=this.props.helpText;if(!!!e)return null;var t=\"object\"===typeof e&&!a.isValidElement(e);return e&&t?a.createElement(Pe,Ce({},e)):a.createElement(Pe,null,e)},t.prototype.render=function(){var e=this,t=this.props,n=t.placeholder,r=t.children,o=t.value,i=t.outlined,u=t.label,s=void 0===u?\"\":u,c=t.options,f=void 0===c?[]:c,d=t.rootProps,p=void 0===d?{}:d,h=t.className,m=t.enhanced,y=(t.icon,t.withLeadingIcon,t.onChange,t.onFocus,t.onBlur,t.onKeyDown,t.invalid),v=t.inputRef,g=(t.helpText,Oe(t,[\"placeholder\",\"children\",\"value\",\"outlined\",\"label\",\"options\",\"rootProps\",\"className\",\"enhanced\",\"icon\",\"withLeadingIcon\",\"onChange\",\"onFocus\",\"onBlur\",\"onKeyDown\",\"invalid\",\"inputRef\",\"helpText\"])),b=this.props,_=b.icon,E=b.withLeadingIcon;void 0!==E&&(Object(l.a)(\"Select prop 'withLeadingIcon' is now 'icon'.\"),_=E);var T=function e(t){return Array.isArray(t)&&t[0]&&\"object\"===typeof t[0]?t.map(function(t){if(\"object\"!==typeof t)throw new Error(\"Encountered non object for Select \"+t);return Ce({},t,{options:e(t.options)})}):Array.isArray(t)?t.map(function(e){return{value:e,label:e}}):\"object\"===typeof t?Object.keys(t).map(function(e){return{value:e,label:t[e]}}):t}(f),C=void 0!==o?void 0:this.props.defaultValue||\"\",O={onChange:this.handleChange,onFocus:this.handleFocus,onBlur:this.handleBlur,onTouchStart:this.handleClick,onMouseDown:this.handleClick},w={defaultValue:C,value:o,placeholder:n,selectOptions:T},x=a.createElement(S,Ce({},this.label.props({}),{ref:this.label.setRef}),s);return a.createElement(a.Fragment,null,a.createElement(we,Ce({ripple:!i},this.root.props(Ce({className:h},p)),{invalid:y,required:g.required,icon:_,outlined:i,ref:this.root.setRef}),!!_&&this.renderIcon(_,\"leadingIcon_\"),a.createElement(Se,null),m?a.createElement(a.Fragment,null,a.createElement(\"input\",{type:\"hidden\",ref:function(t){return e.hiddenInput_=t}}),a.createElement(\"div\",Ce({ref:function(t){return e.selectedText=t},className:\"mdc-select__selected-text\",tabIndex:this.props.disabled?-1:0,\"aria-disabled\":this.props.disabled?\"true\":\"false\",\"aria-expanded\":this.state.menuOpen,onKeyDown:this.handleKeydown},O),this.state.selectedTextContent),a.createElement(Ie,Ce({},w,{selectedIndex:this.state.selectedIndex,apiRef:function(t){e.menu=t},open:this.state.menuOpen,onClose:this.handleMenuClosed,onOpen:this.handleMenuOpened,onSelect:this.handleMenuSelected}),r)):a.createElement(xe,Ce({},g,{elementRef:function(t){e.nativeControl=t,v&&v(t)}},w,O),r),i?a.createElement(W,Ce({},this.outline.props({})),x):a.createElement(a.Fragment,null,x,a.createElement(L,Ce({},this.lineRipple.props({}))))),this.renderHelpText())},t}(b.a),ke=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.root=t.createElement(\"root\"),t}return Te(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new y({getAttr:function(t){return e.root.getProp(t)},setAttr:function(t,n){return e.root.setProp(t,n)},removeAttr:function(t){return e.root.removeProp(t)},setContent:function(t){e.root.ref&&(e.root.ref.textContent=t)},registerInteractionHandler:function(t,n){return e.root.addEventListener(t,n)},deregisterInteractionHandler:function(t,n){return e.root.removeEventListener(t,n)},notifyIconAction:function(){return e.emit(\"onClick\",{},!0)}})},t.prototype.render=function(){return a.createElement(R.a,Ce({},this.root.props(Ce({},this.props,{className:\"mdc-select__icon\"}))))},t.displayName=\"SelectIcon\",t}(b.a),Pe=Object(v.a)({displayName:\"SelectHelperText\",tag:\"p\",classNames:function(e){return[\"mdc-select-helper-text\",{\"mdc-select-helper-text--persistent\":e.persistent,\"mdc-select-helper-text--validation-msg\":e.validationMsg}]},consumeProps:[\"persistent\",\"validationMsg\"]}),Ne=function(e){var t=e.enhanced,n=Oe(e,[\"enhanced\"]);return a.createElement(Ae,Ce({key:t?\"enhanced\":\"native\",enhanced:t},n))};Ne.displayName=\"Select\"},,function(e,t,n){\"use strict\";var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null===e||void 0===e)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String(\"abc\");if(e[5]=\"de\",\"5\"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t[\"_\"+String.fromCharCode(n)]=n;if(\"0123456789\"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(\"\"))return!1;var r={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(e){r[e]=e}),\"abcdefghijklmnopqrst\"===Object.keys(Object.assign({},r)).join(\"\")}catch(o){return!1}}()?Object.assign:function(e,t){for(var n,l,u=a(e),s=1;s<arguments.length;s++){for(var c in n=Object(arguments[s]))o.call(n,c)&&(u[c]=n[c]);if(r){l=r(n);for(var f=0;f<l.length;f++)i.call(n,l[f])&&(u[l[f]]=n[l[f]])}}return u}},function(e,t,n){\"use strict\";var r;Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var o=((r=n(63))&&r.__esModule?r:{default:r}).default;t.default=o},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),Object.defineProperty(t,\"useMount\",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(t,\"useUpdate\",{enumerable:!0,get:function(){return o.default}});var r=i(n(68)),o=i(n(69));function i(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r={\"night-dark\":{base:\"vs-dark\",inherit:!0,rules:[],colors:{\"editor.background\":\"#202124\"}}};t.default=r},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){\"use strict\";var r=n(0),o=n(8),i=n.n(o),a={blur:\"onBlur\",cancel:\"onCancel\",click:\"onClick\",close:\"onClose\",contextmenu:\"onContextMenu\",copy:\"onCopy\",cut:\"onCut\",auxclick:\"onAuxClick\",doubleclick:\"onDoubleClick\",dragend:\"onDragEnd\",dragstart:\"onDragStart\",drop:\"onDrop\",focus:\"onFocus\",input:\"onInput\",invalid:\"onInvalid\",keydown:\"onKeyDown\",keypress:\"onKeyPress\",keyup:\"onKeyUp\",mousedown:\"onMouseDown\",mouseup:\"onMouseUp\",paste:\"onPaste\",pause:\"onPause\",play:\"onPlay\",pointercancel:\"onPointerCancel\",pointerdown:\"onPointerDown\",pointerup:\"onPointerUp\",ratechange:\"onRateChange\",reset:\"onReset\",seeked:\"onSeeked\",submit:\"onSubmit\",touchcancel:\"onTouchCancel\",touchend:\"onTouchEnd\",touchstart:\"onTouchStart\",volumechange:\"onVolumeChange\",abort:\"onAbort\",animationend:\"onAnimationEnd\",animationiteration:\"onAnimationIteration\",animationstart:\"onAnimationStart\",canplay:\"onCanPlay\",canplaythrough:\"onCanPlayThrough\",drag:\"onDrag\",dragenter:\"onDragEnter\",dragexit:\"onDragExit\",dragleave:\"onDragLeave\",dragover:\"onDragOver\",durationchange:\"onDurationChange\",emptied:\"onEmptied\",encrypted:\"onEncrypted\",ended:\"onEnded\",error:\"onError\",gotpointercapture:\"onGotPointerCapture\",load:\"onLoad\",loadeddata:\"onLoadedData\",loadedmetadata:\"onLoadedMetadata\",loadstart:\"onLoadStart\",lostpointercapture:\"onLostPointerCapture\",mousemove:\"onMouseMove\",mouseout:\"onMouseOut\",mouseover:\"onMouseOver\",playing:\"onPlaying\",pointermove:\"onPointerMove\",pointerout:\"onPointerOut\",pointerover:\"onPointerOver\",progress:\"onProgress\",scroll:\"onScroll\",seeking:\"onSeeking\",stalled:\"onStalled\",suspend:\"onSuspend\",timeupdate:\"onTimeUpdate\",toggle:\"onToggle\",touchmove:\"onTouchMove\",transitionend:\"onTransitionEnd\",waiting:\"onWaiting\",wheel:\"onWheel\",mouseenter:\"onMouseEnter\",mouseleave:\"onMouseLeave\",pointerenter:\"onPointerEnter\",pointerleave:\"onPointerLeave\",change:\"onChange\",select:\"onSelect\",beforeinput:\"onBeforeInput\",compositionend:\"onCompositionEnd\",compositionstart:\"onCompositionStart\",compositionupdate:\"onCompositionUpdate\"},l=function(e,t){var n;return function(){var r=this,o=arguments,i=function(){n=null,e.apply(r,o)};null!==n&&clearTimeout(n),n=setTimeout(i,t)}},u=n(22);n.d(t,\"a\",function(){return m});var s=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},f=function(e,t){var n=\"function\"===typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(l){o={error:l}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a},d=function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(f(arguments[t]));return e},p=function(e){return a[e]||e},h=function(){function e(e){this._classes=new Set,this._events={},this._style={},this._props={},this._ref=null,this._onChange=null,this._onChange=e,this.onChange=this.onChange.bind(this),this.addClass=this.addClass.bind(this),this.removeClass=this.removeClass.bind(this),this.hasClass=this.hasClass.bind(this),this.setProp=this.setProp.bind(this),this.getProp=this.getProp.bind(this),this.removeProp=this.removeProp.bind(this),this.setStyle=this.setStyle.bind(this),this.addEventListener=this.addEventListener.bind(this),this.removeEventListener=this.removeEventListener.bind(this),this.setRef=this.setRef.bind(this)}return e.prototype.onChange=function(){this._onChange&&this._onChange()},e.prototype.destroy=function(){this._onChange=null,this._ref=null,this._events={},this._style={},this._props={},this._classes=new Set},e.prototype.addClass=function(e){this._classes.has(e)||(this._classes.add(e),this.onChange())},e.prototype.removeClass=function(e){this._classes.has(e)&&(this._classes.delete(e),this.onChange())},e.prototype.hasClass=function(e){return this._classes.has(e)},e.prototype.setProp=function(e,t){this._props[e]!==t&&(this._props[e]=t,this.onChange())},e.prototype.getProp=function(e){return this._props[e]},e.prototype.removeProp=function(e){void 0!==this._props[e]&&(delete this._props[e],this.onChange())},e.prototype.props=function(e){var t=this,n=e.className,r=void 0===n?\"\":n,o=e.style,a=void 0===o?{}:o,l=Object.entries(e).reduce(function(e,n){var r=f(n,2),o=r[0],i=r[1],a=t._events[o];if(\"function\"===typeof i&&\"function\"===typeof a){e[o]=function(e){return a(e),i(e)}}return e},c({},this._events)),u=i()(r,d(this._classes)),s=c({},this._style,a);return c({},e,this._props,l,{style:s,className:u})},e.prototype.setStyle=function(e,t){e=e.startsWith(\"--\")?e:Object(u.a)(e),this._style[e]!==t&&(this._style[e]=t,this.onChange())},e.prototype.addEventListener=function(e,t){var n=p(e);this._events[n]!==t&&(this._events[n]=t,this.onChange())},e.prototype.removeEventListener=function(e,t){var n=p(e);this._events[n]&&(delete this._events[n],this.onChange())},e.prototype.setRef=function(e){e&&(this._ref=e)},Object.defineProperty(e.prototype,\"ref\",{get:function(){return this._ref},enumerable:!0,configurable:!0}),e}(),m=function(e){function t(t){var n=e.call(this,t)||this;return n.elements={},n.constructor.shouldDebounce?n.update=l(n.update.bind(n),0):n.update=n.update.bind(n),n}return s(t,e),t.prototype.componentDidMount=function(){this.foundation=this.getDefaultFoundation(),this.foundation.init(),this.sync(this.props,{})},t.prototype.componentDidUpdate=function(e){this.sync(this.props,e)},t.prototype.componentWillUnmount=function(){this.foundation&&this.foundation.destroy(),this.foundation=void 0,Object.values(this.elements).forEach(function(e){return e.destroy()})},t.prototype.createElement=function(e){var t=new h(this.update);return this.elements[e]=t,t},t.prototype.update=function(){this.foundation&&this.setState({})},t.prototype.sync=function(e,t){},t.prototype.syncProp=function(e,t,n){(void 0!==e||void 0!==t&&void 0===e)&&e!==t&&n()},t.prototype.getDefaultFoundation=function(){return{init:function(){},destroy:function(){}}},t.prototype.emit=function(e,t,n){var r;void 0===n&&(n=!1),r=new CustomEvent(e,{detail:t,bubbles:n}),Object.defineProperty(r,\"target\",{value:t,writable:!1}),Object.defineProperty(r,\"currentTarget\",{value:t,writable:!1});var o=e;return this.foundation&&this.props[o]&&this.props[o](r),r},t.shouldDebounce=!1,t}(r.Component)},function(e,t,n){var r,o=n(76),i=n(77),a=function(){var e=[];return{activateTrap:function(t){if(e.length>0){var n=e[e.length-1];n!==t&&n.pause()}var r=e.indexOf(t);-1===r?e.push(t):(e.splice(r,1),e.push(t))},deactivateTrap:function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1),e.length>0&&e[e.length-1].unpause()}}}();function l(e){return setTimeout(e,0)}e.exports=function(e,t){var n=document,u=\"string\"===typeof e?n.querySelector(e):e,s=i({returnFocusOnDeactivate:!0,escapeDeactivates:!0},t),c={firstTabbableNode:null,lastTabbableNode:null,nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1},f={activate:function(e){if(c.active)return;E(),c.active=!0,c.paused=!1,c.nodeFocusedBeforeActivation=n.activeElement;var t=e&&e.onActivate?e.onActivate:s.onActivate;t&&t();return p(),f},deactivate:d,pause:function(){if(c.paused||!c.active)return;c.paused=!0,h()},unpause:function(){if(!c.paused||!c.active)return;c.paused=!1,E(),p()}};return f;function d(e){if(c.active){clearTimeout(r),h(),c.active=!1,c.paused=!1,a.deactivateTrap(f);var t=e&&void 0!==e.onDeactivate?e.onDeactivate:s.onDeactivate;return t&&t(),(e&&void 0!==e.returnFocus?e.returnFocus:s.returnFocusOnDeactivate)&&l(function(){T(c.nodeFocusedBeforeActivation)}),f}}function p(){if(c.active)return a.activateTrap(f),r=l(function(){T(y())}),n.addEventListener(\"focusin\",g,!0),n.addEventListener(\"mousedown\",v,{capture:!0,passive:!1}),n.addEventListener(\"touchstart\",v,{capture:!0,passive:!1}),n.addEventListener(\"click\",_,{capture:!0,passive:!1}),n.addEventListener(\"keydown\",b,{capture:!0,passive:!1}),f}function h(){if(c.active)return n.removeEventListener(\"focusin\",g,!0),n.removeEventListener(\"mousedown\",v,!0),n.removeEventListener(\"touchstart\",v,!0),n.removeEventListener(\"click\",_,!0),n.removeEventListener(\"keydown\",b,!0),f}function m(e){var t=s[e],r=t;if(!t)return null;if(\"string\"===typeof t&&!(r=n.querySelector(t)))throw new Error(\"`\"+e+\"` refers to no known node\");if(\"function\"===typeof t&&!(r=t()))throw new Error(\"`\"+e+\"` did not return a node\");return r}function y(){var e;if(!(e=null!==m(\"initialFocus\")?m(\"initialFocus\"):u.contains(n.activeElement)?n.activeElement:c.firstTabbableNode||m(\"fallbackFocus\")))throw new Error(\"You can't have a focus-trap without at least one focusable element\");return e}function v(e){u.contains(e.target)||(s.clickOutsideDeactivates?d({returnFocus:!o.isFocusable(e.target)}):s.allowOutsideClick&&s.allowOutsideClick(e)||e.preventDefault())}function g(e){u.contains(e.target)||e.target instanceof Document||(e.stopImmediatePropagation(),T(c.mostRecentlyFocusedNode||y()))}function b(e){if(!1!==s.escapeDeactivates&&function(e){return\"Escape\"===e.key||\"Esc\"===e.key||27===e.keyCode}(e))return e.preventDefault(),void d();(function(e){return\"Tab\"===e.key||9===e.keyCode})(e)&&function(e){if(E(),e.shiftKey&&e.target===c.firstTabbableNode)return e.preventDefault(),void T(c.lastTabbableNode);if(!e.shiftKey&&e.target===c.lastTabbableNode)e.preventDefault(),T(c.firstTabbableNode)}(e)}function _(e){s.clickOutsideDeactivates||u.contains(e.target)||s.allowOutsideClick&&s.allowOutsideClick(e)||(e.preventDefault(),e.stopImmediatePropagation())}function E(){var e=o(u);c.firstTabbableNode=e[0]||y(),c.lastTabbableNode=e[e.length-1]||y()}function T(e){e!==n.activeElement&&(e&&e.focus?(e.focus(),c.mostRecentlyFocusedNode=e,function(e){return e.tagName&&\"input\"===e.tagName.toLowerCase()&&\"function\"===typeof e.select}(e)&&e.select()):T(y()))}}},function(e,t,n){\"use strict\";var r=n(0);t.a=function(){var e=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).liveMeasure,t=void 0===e||e,n=Object(r.useState)({}),o=n[0],i=n[1],a=Object(r.useState)(null),l=a[0],u=a[1],s=Object(r.useCallback)(function(e){u(e)},[]);return Object(r.useLayoutEffect)(function(){if(l){var e=function(){return window.requestAnimationFrame(function(){return i(function(e){var t=e.getBoundingClientRect();return{width:t.width,height:t.height,top:\"x\"in t?t.x:t.top,left:\"y\"in t?t.y:t.left,x:\"x\"in t?t.x:t.left,y:\"y\"in t?t.y:t.top,right:t.right,bottom:t.bottom}}(l))})};if(e(),t)return window.addEventListener(\"resize\",e),window.addEventListener(\"scroll\",e),function(){window.removeEventListener(\"resize\",e),window.removeEventListener(\"scroll\",e)}}},[l]),[s,o,l]}},,function(e,t,n){\"use strict\";var r=n(0),o=n(6),i=n(13),a=n(7),l=n(14),u=n(1),s=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),c={ICON_BUTTON_ON:\"mdc-icon-button--on\",ROOT:\"mdc-icon-button\"},f={ARIA_PRESSED:\"aria-pressed\",CHANGE_EVENT:\"MDCIconButtonToggle:change\"},d=function(e){function t(n){return e.call(this,u.a({},t.defaultAdapter,n))||this}return u.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return c},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return f},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},hasClass:function(){return!1},notifyChange:function(){},removeClass:function(){},setAttr:function(){}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){this.adapter_.setAttr(f.ARIA_PRESSED,\"\"+this.isOn())},t.prototype.handleClick=function(){this.toggle(),this.adapter_.notifyChange({isOn:this.isOn()})},t.prototype.isOn=function(){return this.adapter_.hasClass(c.ICON_BUTTON_ON)},t.prototype.toggle=function(e){void 0===e&&(e=!this.isOn()),e?this.adapter_.addClass(c.ICON_BUTTON_ON):this.adapter_.removeClass(c.ICON_BUTTON_ON),this.adapter_.setAttr(f.ARIA_PRESSED,\"\"+e)},t}(s),p=n(36),h=n(5),m=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),y=function(){return(y=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},v=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},g=Object(a.b)({surface:!1,unbounded:!0})(Object(o.a)({displayName:\"IconButtonRoot\",tag:h.a,classNames:function(e){return[\"mdc-icon-button\",{\"mdc-icon-button--on\":e.checked}]},defaultProps:{role:\"button\",tabIndex:\"0\"},consumeProps:[\"checked\"]})),b=Object(a.b)({surface:!1,unbounded:!0})(Object(o.a)({displayName:\"IconButtonRoot\",tag:\"button\",classNames:function(e){return[\"mdc-icon-button\",{\"mdc-icon-button--on\":e.checked}]},defaultProps:{role:\"button\",tabIndex:\"0\"},consumeProps:[\"checked\"]})),_=Object(o.a)({displayName:\"IconButtonIcon\",tag:h.a,classNames:function(e){return[\"mdc-icon-button__icon\",{\"mdc-icon-button__icon--on\":e.on}]},consumeProps:[\"on\"]}),E=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.handleClick=n.handleClick.bind(n),n}return m(t,e),Object.defineProperty(t.prototype,\"on\",{get:function(){return this.foundation&&this.foundation.isOn()},set:function(e){this.foundation.toggle(e)},enumerable:!0,configurable:!0}),t.prototype.getDefaultFoundation=function(){var e=this;return new d({addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},hasClass:function(t){return e.root.hasClass(t)},setAttr:function(t,n){return e.root.setProp(t,n)},notifyChange:function(t){return e.emit(\"onChange\",t)}})},t.prototype.isOn=function(){return void 0!==this.props.checked?this.props.checked:this.on},t.prototype.sync=function(e){void 0!==e.checked&&this.on!==e.checked&&(this.on=!!e.checked)},t.prototype.handleClick=function(e){this.props.onClick&&this.props.onClick(e),this.foundation.handleClick()},t.prototype.render=function(){var e=this.props,t=e.icon,n=e.iconOptions,o=e.onIcon,a=e.onIconOptions,l=v(e,[\"icon\",\"iconOptions\",\"onIcon\",\"onIconOptions\"]);return(n||a)&&Object(i.a)(\"IconButton component props iconOptions and onIconOptions must be passed directly to the icon and onIcon prop. This issue has NOT been automatically fixed for you, please update your code.\"),r.createElement(b,y({\"aria-pressed\":this.isOn(),\"aria-hidden\":\"true\"},this.root.props(l),{tag:\"button\",onClick:this.handleClick}),r.createElement(_,{icon:t}),r.createElement(_,{icon:o,on:!0}))},t.displayName=\"IconButton\",t}(p.a),T=function(e){var t=v(e,[]);return t.onIcon?r.createElement(E,y({},t)):r.createElement(g,y({\"aria-hidden\":\"true\",tag:\"button\"},t))};T.displayName=\"IconButton\",n.d(t,\"a\",function(){return C});var C=Object(o.a)({displayName:\"Card\",classNames:function(e){return[\"mdc-card\",{\"mdc-card--outlined\":e.outlined}]},consumeProps:[\"outlined\"]});Object(o.a)({displayName:\"CardMedia\",tag:\"section\",classNames:function(e){return[\"mdc-card__media\",{\"mdc-card__media--square\":e.square,\"mdc-card__media--16-9\":e.sixteenByNine}]},consumeProps:[\"square\",\"sixteenByNine\"]}),Object(o.a)({displayName:\"CardMediaContent\",classNames:[\"mdc-card__media-content\"]}),Object(a.b)({surface:!1})(Object(o.a)({displayName:\"CardPrimaryAction\",classNames:[\"mdc-card__primary-action\"]})),Object(o.a)({displayName:\"CardActions\",tag:\"section\",classNames:function(e){return[\"mdc-card__actions\",{\"mdc-card__actions--full-bleed\":e.fullBleed}]},consumeProps:[\"fullBleed\"]}),Object(o.a)({displayName:\"CardActionButtons\",classNames:[\"mdc-card__action-buttons\"]}),Object(o.a)({displayName:\"CardActionIcons\",classNames:[\"mdc-card__action-icons\"]}),Object(o.a)({displayName:\"CardActionIcon\",tag:T,classNames:[\"mdc-card__action\",\"mdc-card__action--icon\"]}),Object(o.a)({displayName:\"CardActionButton\",tag:l.a,classNames:[\"mdc-card__action\",\"mdc-card__action--button\"]})},function(e,t,n){\"use strict\";function r(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}n.d(t,\"a\",function(){return r})},function(e,t,n){\"use strict\";var r=n(8),o=n.n(r),i=n(0),a=n.n(i),l=n(3),u=n.n(l);function s(){return(s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function c(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function f(e){var t=e.top,n=void 0===t?0:t,r=e.left,i=void 0===r?0:r,l=e.transform,u=e.className,f=e.children,d=e.innerRef,p=c(e,[\"top\",\"left\",\"transform\",\"className\",\"children\",\"innerRef\"]);return a.a.createElement(\"g\",s({ref:d,className:o()(\"vx-group\",u),transform:l||\"translate(\".concat(i,\", \").concat(n,\")\")},p),f)}function d(e){var t=e.links,n=e.linkComponent,r=e.className;return a.a.createElement(f,null,t.map(function(e,t){return a.a.createElement(f,{className:o()(\"vx-network-links\",r),key:\"network-link-\".concat(t)},a.a.createElement(n,{link:e}))}))}function p(e){var t=e.nodes,n=e.nodeComponent,r=e.className;return a.a.createElement(f,null,t.map(function(e,t){return a.a.createElement(f,{key:\"network-node-\".concat(t),className:o()(\"vx-network-nodes\",r),transform:\"translate(\".concat(e.x,\", \").concat(e.y,\")\")},a.a.createElement(n,{node:e}))}))}function h(e){var t=e.link;return a.a.createElement(\"line\",{x1:t.source.x,y1:t.source.y,x2:t.target.x,y2:t.target.y,strokeWidth:2,stroke:\"#999\",strokeOpacity:.6})}function m(){return a.a.createElement(\"circle\",{r:15,fill:\"#21D4FD\"})}function y(e){var t=e.graph,n=e.linkComponent,r=void 0===n?h:n,o=e.nodeComponent,i=void 0===o?m:o;return a.a.createElement(f,null,a.a.createElement(d,{links:t.links,linkComponent:r}),a.a.createElement(p,{nodes:t.nodes,nodeComponent:i}))}f.propTypes={top:u.a.number,left:u.a.number,transform:u.a.string,className:u.a.string,children:u.a.any,innerRef:u.a.oneOfType([u.a.func,u.a.object])},n.d(t,\"a\",function(){return y}),d.propTypes={links:u.a.array,linkComponent:u.a.any,className:u.a.string},p.propTypes={nodes:u.a.array,nodeComponent:u.a.any,className:u.a.string},h.propTypes={link:u.a.object},y.propTypes={graph:u.a.object,linkComponent:u.a.any,nodeComponent:u.a.any}},,,,,,function(e,t,n){\"use strict\";var r=n(28),o=\"function\"===typeof Symbol&&Symbol.for,i=o?Symbol.for(\"react.element\"):60103,a=o?Symbol.for(\"react.portal\"):60106,l=o?Symbol.for(\"react.fragment\"):60107,u=o?Symbol.for(\"react.strict_mode\"):60108,s=o?Symbol.for(\"react.profiler\"):60114,c=o?Symbol.for(\"react.provider\"):60109,f=o?Symbol.for(\"react.context\"):60110,d=o?Symbol.for(\"react.forward_ref\"):60112,p=o?Symbol.for(\"react.suspense\"):60113,h=o?Symbol.for(\"react.suspense_list\"):60120,m=o?Symbol.for(\"react.memo\"):60115,y=o?Symbol.for(\"react.lazy\"):60116;o&&Symbol.for(\"react.fundamental\"),o&&Symbol.for(\"react.responder\"),o&&Symbol.for(\"react.scope\");var v=\"function\"===typeof Symbol&&Symbol.iterator;function g(e){for(var t=e.message,n=\"https://reactjs.org/docs/error-decoder.html?invariant=\"+t,r=1;r<arguments.length;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r]);return e.message=\"Minified React error #\"+t+\"; visit \"+n+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings. \",e}var b={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_={};function E(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||b}function T(){}function C(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||b}E.prototype.isReactComponent={},E.prototype.setState=function(e,t){if(\"object\"!==typeof e&&\"function\"!==typeof e&&null!=e)throw g(Error(85));this.updater.enqueueSetState(this,e,t,\"setState\")},E.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,\"forceUpdate\")},T.prototype=E.prototype;var O=C.prototype=new T;O.constructor=C,r(O,E.prototype),O.isPureReactComponent=!0;var w={current:null},S={suspense:null},x={current:null},I=Object.prototype.hasOwnProperty,A={key:!0,ref:!0,__self:!0,__source:!0};function k(e,t,n){var r,o={},a=null,l=null;if(null!=t)for(r in void 0!==t.ref&&(l=t.ref),void 0!==t.key&&(a=\"\"+t.key),t)I.call(t,r)&&!A.hasOwnProperty(r)&&(o[r]=t[r]);var u=arguments.length-2;if(1===u)o.children=n;else if(1<u){for(var s=Array(u),c=0;c<u;c++)s[c]=arguments[c+2];o.children=s}if(e&&e.defaultProps)for(r in u=e.defaultProps)void 0===o[r]&&(o[r]=u[r]);return{$$typeof:i,type:e,key:a,ref:l,props:o,_owner:x.current}}function P(e){return\"object\"===typeof e&&null!==e&&e.$$typeof===i}var N=/\\/+/g,L=[];function R(e,t,n,r){if(L.length){var o=L.pop();return o.result=e,o.keyPrefix=t,o.func=n,o.context=r,o.count=0,o}return{result:e,keyPrefix:t,func:n,context:r,count:0}}function D(e){e.result=null,e.keyPrefix=null,e.func=null,e.context=null,e.count=0,10>L.length&&L.push(e)}function M(e,t,n){return null==e?0:function e(t,n,r,o){var l=typeof t;\"undefined\"!==l&&\"boolean\"!==l||(t=null);var u=!1;if(null===t)u=!0;else switch(l){case\"string\":case\"number\":u=!0;break;case\"object\":switch(t.$$typeof){case i:case a:u=!0}}if(u)return r(o,t,\"\"===n?\".\"+j(t,0):n),1;if(u=0,n=\"\"===n?\".\":n+\":\",Array.isArray(t))for(var s=0;s<t.length;s++){var c=n+j(l=t[s],s);u+=e(l,c,r,o)}else if(c=null===t||\"object\"!==typeof t?null:\"function\"===typeof(c=v&&t[v]||t[\"@@iterator\"])?c:null,\"function\"===typeof c)for(t=c.call(t),s=0;!(l=t.next()).done;)u+=e(l=l.value,c=n+j(l,s++),r,o);else if(\"object\"===l)throw r=\"\"+t,g(Error(31),\"[object Object]\"===r?\"object with keys {\"+Object.keys(t).join(\", \")+\"}\":r,\"\");return u}(e,\"\",t,n)}function j(e,t){return\"object\"===typeof e&&null!==e&&null!=e.key?function(e){var t={\"=\":\"=0\",\":\":\"=2\"};return\"$\"+(\"\"+e).replace(/[=:]/g,function(e){return t[e]})}(e.key):t.toString(36)}function F(e,t){e.func.call(e.context,t,e.count++)}function U(e,t,n){var r=e.result,o=e.keyPrefix;e=e.func.call(e.context,t,e.count++),Array.isArray(e)?B(e,r,n,function(e){return e}):null!=e&&(P(e)&&(e=function(e,t){return{$$typeof:i,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}(e,o+(!e.key||t&&t.key===e.key?\"\":(\"\"+e.key).replace(N,\"$&/\")+\"/\")+n)),r.push(e))}function B(e,t,n,r,o){var i=\"\";null!=n&&(i=(\"\"+n).replace(N,\"$&/\")+\"/\"),M(e,U,t=R(t,i,r,o)),D(t)}function V(){var e=w.current;if(null===e)throw g(Error(321));return e}var H={Children:{map:function(e,t,n){if(null==e)return e;var r=[];return B(e,r,null,t,n),r},forEach:function(e,t,n){if(null==e)return e;M(e,F,t=R(null,null,t,n)),D(t)},count:function(e){return M(e,function(){return null},null)},toArray:function(e){var t=[];return B(e,t,null,function(e){return e}),t},only:function(e){if(!P(e))throw g(Error(143));return e}},createRef:function(){return{current:null}},Component:E,PureComponent:C,createContext:function(e,t){return void 0===t&&(t=null),(e={$$typeof:f,_calculateChangedBits:t,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null}).Provider={$$typeof:c,_context:e},e.Consumer=e},forwardRef:function(e){return{$$typeof:d,render:e}},lazy:function(e){return{$$typeof:y,_ctor:e,_status:-1,_result:null}},memo:function(e,t){return{$$typeof:m,type:e,compare:void 0===t?null:t}},useCallback:function(e,t){return V().useCallback(e,t)},useContext:function(e,t){return V().useContext(e,t)},useEffect:function(e,t){return V().useEffect(e,t)},useImperativeHandle:function(e,t,n){return V().useImperativeHandle(e,t,n)},useDebugValue:function(){},useLayoutEffect:function(e,t){return V().useLayoutEffect(e,t)},useMemo:function(e,t){return V().useMemo(e,t)},useReducer:function(e,t,n){return V().useReducer(e,t,n)},useRef:function(e){return V().useRef(e)},useState:function(e){return V().useState(e)},Fragment:l,Profiler:s,StrictMode:u,Suspense:p,unstable_SuspenseList:h,createElement:k,cloneElement:function(e,t,n){if(null===e||void 0===e)throw g(Error(267),e);var o=r({},e.props),a=e.key,l=e.ref,u=e._owner;if(null!=t){if(void 0!==t.ref&&(l=t.ref,u=x.current),void 0!==t.key&&(a=\"\"+t.key),e.type&&e.type.defaultProps)var s=e.type.defaultProps;for(c in t)I.call(t,c)&&!A.hasOwnProperty(c)&&(o[c]=void 0===t[c]&&void 0!==s?s[c]:t[c])}var c=arguments.length-2;if(1===c)o.children=n;else if(1<c){s=Array(c);for(var f=0;f<c;f++)s[f]=arguments[f+2];o.children=s}return{$$typeof:i,type:e.type,key:a,ref:l,props:o,_owner:u}},createFactory:function(e){var t=k.bind(null,e);return t.type=e,t},isValidElement:P,version:\"16.10.1\",unstable_withSuspenseConfig:function(e,t){var n=S.suspense;S.suspense=void 0===t?null:t;try{e()}finally{S.suspense=n}},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentDispatcher:w,ReactCurrentBatchConfig:S,ReactCurrentOwner:x,IsSomeRendererActing:{current:!1},assign:r}},z={default:H},K=z&&H||z;e.exports=K.default||K},function(e,t,n){\"use strict\";var r=n(0),o=n(28),i=n(50);function a(e){for(var t=e.message,n=\"https://reactjs.org/docs/error-decoder.html?invariant=\"+t,r=1;r<arguments.length;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r]);return e.message=\"Minified React error #\"+t+\"; visit \"+n+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings. \",e}if(!r)throw a(Error(227));var l=null,u={};function s(){if(l)for(var e in u){var t=u[e],n=l.indexOf(e);if(!(-1<n))throw a(Error(96),e);if(!f[n]){if(!t.extractEvents)throw a(Error(97),e);for(var r in f[n]=t,n=t.eventTypes){var o=void 0,i=n[r],s=t,p=r;if(d.hasOwnProperty(p))throw a(Error(99),p);d[p]=i;var h=i.phasedRegistrationNames;if(h){for(o in h)h.hasOwnProperty(o)&&c(h[o],s,p);o=!0}else i.registrationName?(c(i.registrationName,s,p),o=!0):o=!1;if(!o)throw a(Error(98),r,e)}}}}function c(e,t,n){if(p[e])throw a(Error(100),e);p[e]=t,h[e]=t.eventTypes[n].dependencies}var f=[],d={},p={},h={};var m=!1,y=null,v=!1,g=null,b={onError:function(e){m=!0,y=e}};function _(e,t,n,r,o,i,a,l,u){m=!1,y=null,function(e,t,n,r,o,i,a,l,u){var s=Array.prototype.slice.call(arguments,3);try{t.apply(n,s)}catch(c){this.onError(c)}}.apply(b,arguments)}var E=null,T=null,C=null;function O(e,t,n){var r=e.type||\"unknown-event\";e.currentTarget=C(n),function(e,t,n,r,o,i,l,u,s){if(_.apply(this,arguments),m){if(!m)throw a(Error(198));var c=y;m=!1,y=null,v||(v=!0,g=c)}}(r,t,void 0,e),e.currentTarget=null}function w(e,t){if(null==t)throw a(Error(30));return null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}function S(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}var x=null;function I(e){if(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t))for(var r=0;r<t.length&&!e.isPropagationStopped();r++)O(e,t[r],n[r]);else t&&O(e,t,n);e._dispatchListeners=null,e._dispatchInstances=null,e.isPersistent()||e.constructor.release(e)}}function A(e){if(null!==e&&(x=w(x,e)),e=x,x=null,e){if(S(e,I),x)throw a(Error(95));if(v)throw e=g,v=!1,g=null,e}}var k={injectEventPluginOrder:function(e){if(l)throw a(Error(101));l=Array.prototype.slice.call(e),s()},injectEventPluginsByName:function(e){var t,n=!1;for(t in e)if(e.hasOwnProperty(t)){var r=e[t];if(!u.hasOwnProperty(t)||u[t]!==r){if(u[t])throw a(Error(102),t);u[t]=r,n=!0}}n&&s()}};function P(e,t){var n=e.stateNode;if(!n)return null;var r=E(n);if(!r)return null;n=r[t];e:switch(t){case\"onClick\":case\"onClickCapture\":case\"onDoubleClick\":case\"onDoubleClickCapture\":case\"onMouseDown\":case\"onMouseDownCapture\":case\"onMouseMove\":case\"onMouseMoveCapture\":case\"onMouseUp\":case\"onMouseUpCapture\":(r=!r.disabled)||(r=!(\"button\"===(e=e.type)||\"input\"===e||\"select\"===e||\"textarea\"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&\"function\"!==typeof n)throw a(Error(231),t,typeof n);return n}var N=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;N.hasOwnProperty(\"ReactCurrentDispatcher\")||(N.ReactCurrentDispatcher={current:null}),N.hasOwnProperty(\"ReactCurrentBatchConfig\")||(N.ReactCurrentBatchConfig={suspense:null});var L=/^(.*)[\\\\\\/]/,R=\"function\"===typeof Symbol&&Symbol.for,D=R?Symbol.for(\"react.element\"):60103,M=R?Symbol.for(\"react.portal\"):60106,j=R?Symbol.for(\"react.fragment\"):60107,F=R?Symbol.for(\"react.strict_mode\"):60108,U=R?Symbol.for(\"react.profiler\"):60114,B=R?Symbol.for(\"react.provider\"):60109,V=R?Symbol.for(\"react.context\"):60110,H=R?Symbol.for(\"react.concurrent_mode\"):60111,z=R?Symbol.for(\"react.forward_ref\"):60112,K=R?Symbol.for(\"react.suspense\"):60113,W=R?Symbol.for(\"react.suspense_list\"):60120,G=R?Symbol.for(\"react.memo\"):60115,q=R?Symbol.for(\"react.lazy\"):60116;R&&Symbol.for(\"react.fundamental\"),R&&Symbol.for(\"react.responder\"),R&&Symbol.for(\"react.scope\");var X=\"function\"===typeof Symbol&&Symbol.iterator;function Y(e){return null===e||\"object\"!==typeof e?null:\"function\"===typeof(e=X&&e[X]||e[\"@@iterator\"])?e:null}function Q(e){if(null==e)return null;if(\"function\"===typeof e)return e.displayName||e.name||null;if(\"string\"===typeof e)return e;switch(e){case j:return\"Fragment\";case M:return\"Portal\";case U:return\"Profiler\";case F:return\"StrictMode\";case K:return\"Suspense\";case W:return\"SuspenseList\"}if(\"object\"===typeof e)switch(e.$$typeof){case V:return\"Context.Consumer\";case B:return\"Context.Provider\";case z:var t=e.render;return t=t.displayName||t.name||\"\",e.displayName||(\"\"!==t?\"ForwardRef(\"+t+\")\":\"ForwardRef\");case G:return Q(e.type);case q:if(e=1===e._status?e._result:null)return Q(e)}return null}function $(e){var t=\"\";do{e:switch(e.tag){case 3:case 4:case 6:case 7:case 10:case 9:var n=\"\";break e;default:var r=e._debugOwner,o=e._debugSource,i=Q(e.type);n=null,r&&(n=Q(r.type)),r=i,i=\"\",o?i=\" (at \"+o.fileName.replace(L,\"\")+\":\"+o.lineNumber+\")\":n&&(i=\" (created by \"+n+\")\"),n=\"\\n    in \"+(r||\"Unknown\")+i}t+=n,e=e.return}while(e);return t}var Z=!(\"undefined\"===typeof window||\"undefined\"===typeof window.document||\"undefined\"===typeof window.document.createElement),J=null,ee=null,te=null;function ne(e){if(e=T(e)){if(\"function\"!==typeof J)throw a(Error(280));var t=E(e.stateNode);J(e.stateNode,e.type,t)}}function re(e){ee?te?te.push(e):te=[e]:ee=e}function oe(){if(ee){var e=ee,t=te;if(te=ee=null,ne(e),t)for(e=0;e<t.length;e++)ne(t[e])}}function ie(e,t){return e(t)}function ae(e,t,n,r){return e(t,n,r)}function le(){}var ue=ie,se=!1,ce=!1;function fe(){null===ee&&null===te||(le(),oe())}new Map,new Map,new Map;var de=/^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$/,pe=Object.prototype.hasOwnProperty,he={},me={};function ye(e,t,n,r,o,i){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i}var ve={};\"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style\".split(\" \").forEach(function(e){ve[e]=new ye(e,0,!1,e,null,!1)}),[[\"acceptCharset\",\"accept-charset\"],[\"className\",\"class\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"]].forEach(function(e){var t=e[0];ve[t]=new ye(t,1,!1,e[1],null,!1)}),[\"contentEditable\",\"draggable\",\"spellCheck\",\"value\"].forEach(function(e){ve[e]=new ye(e,2,!1,e.toLowerCase(),null,!1)}),[\"autoReverse\",\"externalResourcesRequired\",\"focusable\",\"preserveAlpha\"].forEach(function(e){ve[e]=new ye(e,2,!1,e,null,!1)}),\"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope\".split(\" \").forEach(function(e){ve[e]=new ye(e,3,!1,e.toLowerCase(),null,!1)}),[\"checked\",\"multiple\",\"muted\",\"selected\"].forEach(function(e){ve[e]=new ye(e,3,!0,e,null,!1)}),[\"capture\",\"download\"].forEach(function(e){ve[e]=new ye(e,4,!1,e,null,!1)}),[\"cols\",\"rows\",\"size\",\"span\"].forEach(function(e){ve[e]=new ye(e,6,!1,e,null,!1)}),[\"rowSpan\",\"start\"].forEach(function(e){ve[e]=new ye(e,5,!1,e.toLowerCase(),null,!1)});var ge=/[\\-:]([a-z])/g;function be(e){return e[1].toUpperCase()}function _e(e){switch(typeof e){case\"boolean\":case\"number\":case\"object\":case\"string\":case\"undefined\":return e;default:return\"\"}}function Ee(e,t,n,r){var o=ve.hasOwnProperty(t)?ve[t]:null;(null!==o?0===o.type:!r&&(2<t.length&&(\"o\"===t[0]||\"O\"===t[0])&&(\"n\"===t[1]||\"N\"===t[1])))||(function(e,t,n,r){if(null===t||\"undefined\"===typeof t||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case\"function\":case\"symbol\":return!0;case\"boolean\":return!r&&(null!==n?!n.acceptsBooleans:\"data-\"!==(e=e.toLowerCase().slice(0,5))&&\"aria-\"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,o,r)&&(n=null),r||null===o?function(e){return!!pe.call(me,e)||!pe.call(he,e)&&(de.test(e)?me[e]=!0:(he[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,\"\"+n)):o.mustUseProperty?e[o.propertyName]=null===n?3!==o.type&&\"\":n:(t=o.attributeName,r=o.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(o=o.type)||4===o&&!0===n?\"\":\"\"+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}function Te(e){var t=e.type;return(e=e.nodeName)&&\"input\"===e.toLowerCase()&&(\"checkbox\"===t||\"radio\"===t)}function Ce(e){e._valueTracker||(e._valueTracker=function(e){var t=Te(e)?\"checked\":\"value\",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=\"\"+e[t];if(!e.hasOwnProperty(t)&&\"undefined\"!==typeof n&&\"function\"===typeof n.get&&\"function\"===typeof n.set){var o=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(e){r=\"\"+e,i.call(this,e)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(e){r=\"\"+e},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function Oe(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r=\"\";return e&&(r=Te(e)?e.checked?\"true\":\"false\":e.value),(e=r)!==n&&(t.setValue(e),!0)}function we(e,t){var n=t.checked;return o({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=n?n:e._wrapperState.initialChecked})}function Se(e,t){var n=null==t.defaultValue?\"\":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=_e(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:\"checkbox\"===t.type||\"radio\"===t.type?null!=t.checked:null!=t.value}}function xe(e,t){null!=(t=t.checked)&&Ee(e,\"checked\",t,!1)}function Ie(e,t){xe(e,t);var n=_e(t.value),r=t.type;if(null!=n)\"number\"===r?(0===n&&\"\"===e.value||e.value!=n)&&(e.value=\"\"+n):e.value!==\"\"+n&&(e.value=\"\"+n);else if(\"submit\"===r||\"reset\"===r)return void e.removeAttribute(\"value\");t.hasOwnProperty(\"value\")?ke(e,t.type,n):t.hasOwnProperty(\"defaultValue\")&&ke(e,t.type,_e(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function Ae(e,t,n){if(t.hasOwnProperty(\"value\")||t.hasOwnProperty(\"defaultValue\")){var r=t.type;if(!(\"submit\"!==r&&\"reset\"!==r||void 0!==t.value&&null!==t.value))return;t=\"\"+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}\"\"!==(n=e.name)&&(e.name=\"\"),e.defaultChecked=!e.defaultChecked,e.defaultChecked=!!e._wrapperState.initialChecked,\"\"!==n&&(e.name=n)}function ke(e,t,n){\"number\"===t&&e.ownerDocument.activeElement===e||(null==n?e.defaultValue=\"\"+e._wrapperState.initialValue:e.defaultValue!==\"\"+n&&(e.defaultValue=\"\"+n))}function Pe(e,t){return e=o({children:void 0},t),(t=function(e){var t=\"\";return r.Children.forEach(e,function(e){null!=e&&(t+=e)}),t}(t.children))&&(e.children=t),e}function Ne(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o<n.length;o++)t[\"$\"+n[o]]=!0;for(n=0;n<e.length;n++)o=t.hasOwnProperty(\"$\"+e[n].value),e[n].selected!==o&&(e[n].selected=o),o&&r&&(e[n].defaultSelected=!0)}else{for(n=\"\"+_e(n),t=null,o=0;o<e.length;o++){if(e[o].value===n)return e[o].selected=!0,void(r&&(e[o].defaultSelected=!0));null!==t||e[o].disabled||(t=e[o])}null!==t&&(t.selected=!0)}}function Le(e,t){if(null!=t.dangerouslySetInnerHTML)throw a(Error(91));return o({},t,{value:void 0,defaultValue:void 0,children:\"\"+e._wrapperState.initialValue})}function Re(e,t){var n=t.value;if(null==n){if(n=t.defaultValue,null!=(t=t.children)){if(null!=n)throw a(Error(92));if(Array.isArray(t)){if(!(1>=t.length))throw a(Error(93));t=t[0]}n=t}null==n&&(n=\"\")}e._wrapperState={initialValue:_e(n)}}function De(e,t){var n=_e(t.value),r=_e(t.defaultValue);null!=n&&((n=\"\"+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=\"\"+r)}function Me(e){var t=e.textContent;t===e._wrapperState.initialValue&&\"\"!==t&&null!==t&&(e.value=t)}\"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height\".split(\" \").forEach(function(e){var t=e.replace(ge,be);ve[t]=new ye(t,1,!1,e,null,!1)}),\"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type\".split(\" \").forEach(function(e){var t=e.replace(ge,be);ve[t]=new ye(t,1,!1,e,\"http://www.w3.org/1999/xlink\",!1)}),[\"xml:base\",\"xml:lang\",\"xml:space\"].forEach(function(e){var t=e.replace(ge,be);ve[t]=new ye(t,1,!1,e,\"http://www.w3.org/XML/1998/namespace\",!1)}),[\"tabIndex\",\"crossOrigin\"].forEach(function(e){ve[e]=new ye(e,1,!1,e.toLowerCase(),null,!1)}),ve.xlinkHref=new ye(\"xlinkHref\",1,!1,\"xlink:href\",\"http://www.w3.org/1999/xlink\",!0),[\"src\",\"href\",\"action\",\"formAction\"].forEach(function(e){ve[e]=new ye(e,1,!1,e.toLowerCase(),null,!0)});var je={html:\"http://www.w3.org/1999/xhtml\",mathml:\"http://www.w3.org/1998/Math/MathML\",svg:\"http://www.w3.org/2000/svg\"};function Fe(e){switch(e){case\"svg\":return\"http://www.w3.org/2000/svg\";case\"math\":return\"http://www.w3.org/1998/Math/MathML\";default:return\"http://www.w3.org/1999/xhtml\"}}function Ue(e,t){return null==e||\"http://www.w3.org/1999/xhtml\"===e?Fe(t):\"http://www.w3.org/2000/svg\"===e&&\"foreignObject\"===t?\"http://www.w3.org/1999/xhtml\":e}var Be,Ve=function(e){return\"undefined\"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,r,o){MSApp.execUnsafeLocalFunction(function(){return e(t,n)})}:e}(function(e,t){if(e.namespaceURI!==je.svg||\"innerHTML\"in e)e.innerHTML=t;else{for((Be=Be||document.createElement(\"div\")).innerHTML=\"<svg>\"+t.valueOf().toString()+\"</svg>\",t=Be.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function He(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}function ze(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n[\"Webkit\"+e]=\"webkit\"+t,n[\"Moz\"+e]=\"moz\"+t,n}var Ke={animationend:ze(\"Animation\",\"AnimationEnd\"),animationiteration:ze(\"Animation\",\"AnimationIteration\"),animationstart:ze(\"Animation\",\"AnimationStart\"),transitionend:ze(\"Transition\",\"TransitionEnd\")},We={},Ge={};function qe(e){if(We[e])return We[e];if(!Ke[e])return e;var t,n=Ke[e];for(t in n)if(n.hasOwnProperty(t)&&t in Ge)return We[e]=n[t];return e}Z&&(Ge=document.createElement(\"div\").style,\"AnimationEvent\"in window||(delete Ke.animationend.animation,delete Ke.animationiteration.animation,delete Ke.animationstart.animation),\"TransitionEvent\"in window||delete Ke.transitionend.transition);var Xe=qe(\"animationend\"),Ye=qe(\"animationiteration\"),Qe=qe(\"animationstart\"),$e=qe(\"transitionend\"),Ze=\"abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting\".split(\" \"),Je=!1,et=[],tt=null,nt=null,rt=null,ot=new Map,it=new Map,at=\"mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit\".split(\" \"),lt=\"focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture\".split(\" \");function ut(e,t,n,r){return{blockedOn:e,topLevelType:t,eventSystemFlags:32|n,nativeEvent:r}}function st(e,t){switch(e){case\"focus\":case\"blur\":tt=null;break;case\"dragenter\":case\"dragleave\":nt=null;break;case\"mouseover\":case\"mouseout\":rt=null;break;case\"pointerover\":case\"pointerout\":ot.delete(t.pointerId);break;case\"gotpointercapture\":case\"lostpointercapture\":it.delete(t.pointerId)}}function ct(e,t,n,r,o){return null===e||e.nativeEvent!==o?ut(t,n,r,o):(e.eventSystemFlags|=r,e)}function ft(e){if(null!==e.blockedOn)return!1;var t=Cn(e.topLevelType,e.eventSystemFlags,e.nativeEvent);return null===t||(e.blockedOn=t,!1)}function dt(e,t,n){ft(e)&&n.delete(t)}function pt(){for(Je=!1;0<et.length;){var e=et[0];if(null!==e.blockedOn)break;var t=Cn(e.topLevelType,e.eventSystemFlags,e.nativeEvent);null!==t?e.blockedOn=t:et.shift()}null!==tt&&ft(tt)&&(tt=null),null!==nt&&ft(nt)&&(nt=null),null!==rt&&ft(rt)&&(rt=null),ot.forEach(dt),it.forEach(dt)}function ht(e,t){e.blockedOn===t&&(e.blockedOn=null,Je||(Je=!0,i.unstable_scheduleCallback(i.unstable_NormalPriority,pt)))}function mt(e){function t(t){return ht(t,e)}if(0<et.length){ht(et[0],e);for(var n=1;n<et.length;n++){var r=et[n];r.blockedOn===e&&(r.blockedOn=null)}}null!==tt&&ht(tt,e),null!==nt&&ht(nt,e),null!==rt&&ht(rt,e),ot.forEach(t),it.forEach(t)}var yt=0,vt=2,gt=1024;function bt(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do{((t=e).effectTag&(vt|gt))!==yt&&(n=t.return),e=t.return}while(e)}return 3===t.tag?n:null}function _t(e){if(bt(e)!==e)throw a(Error(188))}function Et(e){if(!(e=function(e){var t=e.alternate;if(!t){if(null===(t=bt(e)))throw a(Error(188));return t!==e?null:e}for(var n=e,r=t;;){var o=n.return;if(null===o)break;var i=o.alternate;if(null===i){if(null!==(r=o.return)){n=r;continue}break}if(o.child===i.child){for(i=o.child;i;){if(i===n)return _t(o),e;if(i===r)return _t(o),t;i=i.sibling}throw a(Error(188))}if(n.return!==r.return)n=o,r=i;else{for(var l=!1,u=o.child;u;){if(u===n){l=!0,n=o,r=i;break}if(u===r){l=!0,r=o,n=i;break}u=u.sibling}if(!l){for(u=i.child;u;){if(u===n){l=!0,n=i,r=o;break}if(u===r){l=!0,r=i,n=o;break}u=u.sibling}if(!l)throw a(Error(189))}}if(n.alternate!==r)throw a(Error(190))}if(3!==n.tag)throw a(Error(188));return n.stateNode.current===n?e:t}(e)))return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}function Tt(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}function Ct(e){do{e=e.return}while(e&&5!==e.tag);return e||null}function Ot(e,t,n){(t=P(e,n.dispatchConfig.phasedRegistrationNames[t]))&&(n._dispatchListeners=w(n._dispatchListeners,t),n._dispatchInstances=w(n._dispatchInstances,e))}function wt(e){if(e&&e.dispatchConfig.phasedRegistrationNames){for(var t=e._targetInst,n=[];t;)n.push(t),t=Ct(t);for(t=n.length;0<t--;)Ot(n[t],\"captured\",e);for(t=0;t<n.length;t++)Ot(n[t],\"bubbled\",e)}}function St(e,t,n){e&&n&&n.dispatchConfig.registrationName&&(t=P(e,n.dispatchConfig.registrationName))&&(n._dispatchListeners=w(n._dispatchListeners,t),n._dispatchInstances=w(n._dispatchInstances,e))}function xt(e){e&&e.dispatchConfig.registrationName&&St(e._targetInst,null,e)}function It(e){S(e,wt)}function At(){return!0}function kt(){return!1}function Pt(e,t,n,r){for(var o in this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n,e=this.constructor.Interface)e.hasOwnProperty(o)&&((t=e[o])?this[o]=t(n):\"target\"===o?this.target=r:this[o]=n[o]);return this.isDefaultPrevented=(null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue)?At:kt,this.isPropagationStopped=kt,this}function Nt(e,t,n,r){if(this.eventPool.length){var o=this.eventPool.pop();return this.call(o,e,t,n,r),o}return new this(e,t,n,r)}function Lt(e){if(!(e instanceof this))throw a(Error(279));e.destructor(),10>this.eventPool.length&&this.eventPool.push(e)}function Rt(e){e.eventPool=[],e.getPooled=Nt,e.release=Lt}o(Pt.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():\"unknown\"!==typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=At)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():\"unknown\"!==typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=At)},persist:function(){this.isPersistent=At},isPersistent:kt,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=kt,this._dispatchInstances=this._dispatchListeners=null}}),Pt.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},Pt.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var i=new t;return o(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=o({},r.Interface,e),n.extend=r.extend,Rt(n),n},Rt(Pt);var Dt=Pt.extend({animationName:null,elapsedTime:null,pseudoElement:null}),Mt=Pt.extend({clipboardData:function(e){return\"clipboardData\"in e?e.clipboardData:window.clipboardData}}),jt=Pt.extend({view:null,detail:null}),Ft=jt.extend({relatedTarget:null});function Ut(e){var t=e.keyCode;return\"charCode\"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}var Bt={Esc:\"Escape\",Spacebar:\" \",Left:\"ArrowLeft\",Up:\"ArrowUp\",Right:\"ArrowRight\",Down:\"ArrowDown\",Del:\"Delete\",Win:\"OS\",Menu:\"ContextMenu\",Apps:\"ContextMenu\",Scroll:\"ScrollLock\",MozPrintableKey:\"Unidentified\"},Vt={8:\"Backspace\",9:\"Tab\",12:\"Clear\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"NumLock\",145:\"ScrollLock\",224:\"Meta\"},Ht={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};function zt(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=Ht[e])&&!!t[e]}function Kt(){return zt}for(var Wt=jt.extend({key:function(e){if(e.key){var t=Bt[e.key]||e.key;if(\"Unidentified\"!==t)return t}return\"keypress\"===e.type?13===(e=Ut(e))?\"Enter\":String.fromCharCode(e):\"keydown\"===e.type||\"keyup\"===e.type?Vt[e.keyCode]||\"Unidentified\":\"\"},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:Kt,charCode:function(e){return\"keypress\"===e.type?Ut(e):0},keyCode:function(e){return\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0},which:function(e){return\"keypress\"===e.type?Ut(e):\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0}}),Gt=0,qt=0,Xt=!1,Yt=!1,Qt=jt.extend({screenX:null,screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:Kt,button:null,buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},movementX:function(e){if(\"movementX\"in e)return e.movementX;var t=Gt;return Gt=e.screenX,Xt?\"mousemove\"===e.type?e.screenX-t:0:(Xt=!0,0)},movementY:function(e){if(\"movementY\"in e)return e.movementY;var t=qt;return qt=e.screenY,Yt?\"mousemove\"===e.type?e.screenY-t:0:(Yt=!0,0)}}),$t=Qt.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),Zt=Qt.extend({dataTransfer:null}),Jt=jt.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:Kt}),en=Pt.extend({propertyName:null,elapsedTime:null,pseudoElement:null}),tn=Qt.extend({deltaX:function(e){return\"deltaX\"in e?e.deltaX:\"wheelDeltaX\"in e?-e.wheelDeltaX:0},deltaY:function(e){return\"deltaY\"in e?e.deltaY:\"wheelDeltaY\"in e?-e.wheelDeltaY:\"wheelDelta\"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null}),nn=[[\"blur\",\"blur\",0],[\"cancel\",\"cancel\",0],[\"click\",\"click\",0],[\"close\",\"close\",0],[\"contextmenu\",\"contextMenu\",0],[\"copy\",\"copy\",0],[\"cut\",\"cut\",0],[\"auxclick\",\"auxClick\",0],[\"dblclick\",\"doubleClick\",0],[\"dragend\",\"dragEnd\",0],[\"dragstart\",\"dragStart\",0],[\"drop\",\"drop\",0],[\"focus\",\"focus\",0],[\"input\",\"input\",0],[\"invalid\",\"invalid\",0],[\"keydown\",\"keyDown\",0],[\"keypress\",\"keyPress\",0],[\"keyup\",\"keyUp\",0],[\"mousedown\",\"mouseDown\",0],[\"mouseup\",\"mouseUp\",0],[\"paste\",\"paste\",0],[\"pause\",\"pause\",0],[\"play\",\"play\",0],[\"pointercancel\",\"pointerCancel\",0],[\"pointerdown\",\"pointerDown\",0],[\"pointerup\",\"pointerUp\",0],[\"ratechange\",\"rateChange\",0],[\"reset\",\"reset\",0],[\"seeked\",\"seeked\",0],[\"submit\",\"submit\",0],[\"touchcancel\",\"touchCancel\",0],[\"touchend\",\"touchEnd\",0],[\"touchstart\",\"touchStart\",0],[\"volumechange\",\"volumeChange\",0],[\"drag\",\"drag\",1],[\"dragenter\",\"dragEnter\",1],[\"dragexit\",\"dragExit\",1],[\"dragleave\",\"dragLeave\",1],[\"dragover\",\"dragOver\",1],[\"mousemove\",\"mouseMove\",1],[\"mouseout\",\"mouseOut\",1],[\"mouseover\",\"mouseOver\",1],[\"pointermove\",\"pointerMove\",1],[\"pointerout\",\"pointerOut\",1],[\"pointerover\",\"pointerOver\",1],[\"scroll\",\"scroll\",1],[\"toggle\",\"toggle\",1],[\"touchmove\",\"touchMove\",1],[\"wheel\",\"wheel\",1],[\"abort\",\"abort\",2],[Xe,\"animationEnd\",2],[Ye,\"animationIteration\",2],[Qe,\"animationStart\",2],[\"canplay\",\"canPlay\",2],[\"canplaythrough\",\"canPlayThrough\",2],[\"durationchange\",\"durationChange\",2],[\"emptied\",\"emptied\",2],[\"encrypted\",\"encrypted\",2],[\"ended\",\"ended\",2],[\"error\",\"error\",2],[\"gotpointercapture\",\"gotPointerCapture\",2],[\"load\",\"load\",2],[\"loadeddata\",\"loadedData\",2],[\"loadedmetadata\",\"loadedMetadata\",2],[\"loadstart\",\"loadStart\",2],[\"lostpointercapture\",\"lostPointerCapture\",2],[\"playing\",\"playing\",2],[\"progress\",\"progress\",2],[\"seeking\",\"seeking\",2],[\"stalled\",\"stalled\",2],[\"suspend\",\"suspend\",2],[\"timeupdate\",\"timeUpdate\",2],[$e,\"transitionEnd\",2],[\"waiting\",\"waiting\",2]],rn={},on={},an=0;an<nn.length;an++){var ln=nn[an],un=ln[0],sn=ln[1],cn=ln[2],fn=\"on\"+(sn[0].toUpperCase()+sn.slice(1)),dn={phasedRegistrationNames:{bubbled:fn,captured:fn+\"Capture\"},dependencies:[un],eventPriority:cn};rn[sn]=dn,on[un]=dn}var pn={eventTypes:rn,getEventPriority:function(e){return void 0!==(e=on[e])?e.eventPriority:2},extractEvents:function(e,t,n,r,o){if(!(t=on[e]))return null;switch(e){case\"keypress\":if(0===Ut(r))return null;case\"keydown\":case\"keyup\":e=Wt;break;case\"blur\":case\"focus\":e=Ft;break;case\"click\":if(2===r.button)return null;case\"auxclick\":case\"dblclick\":case\"mousedown\":case\"mousemove\":case\"mouseup\":case\"mouseout\":case\"mouseover\":case\"contextmenu\":e=Qt;break;case\"drag\":case\"dragend\":case\"dragenter\":case\"dragexit\":case\"dragleave\":case\"dragover\":case\"dragstart\":case\"drop\":e=Zt;break;case\"touchcancel\":case\"touchend\":case\"touchmove\":case\"touchstart\":e=Jt;break;case Xe:case Ye:case Qe:e=Dt;break;case $e:e=en;break;case\"scroll\":e=jt;break;case\"wheel\":e=tn;break;case\"copy\":case\"cut\":case\"paste\":e=Mt;break;case\"gotpointercapture\":case\"lostpointercapture\":case\"pointercancel\":case\"pointerdown\":case\"pointermove\":case\"pointerout\":case\"pointerover\":case\"pointerup\":e=$t;break;default:e=Pt}return It(n=e.getPooled(t,n,r,o)),n}},hn=pn.getEventPriority,mn=10,yn=[];function vn(e){var t=e.targetInst,n=t;do{if(!n){e.ancestors.push(n);break}var r=n;if(3===r.tag)r=r.stateNode.containerInfo;else{for(;r.return;)r=r.return;r=3!==r.tag?null:r.stateNode.containerInfo}if(!r)break;var o=n.tag;5!==o&&6!==o||e.ancestors.push(n),n=or(r)}while(n);for(n=0;n<e.ancestors.length;n++){t=e.ancestors[n];var i=Tt(e.nativeEvent);r=e.topLevelType,o=e.eventSystemFlags;for(var a=e.nativeEvent,l=null,u=0;u<f.length;u++){var s=f[u];s&&(s=s.extractEvents(r,o,t,a,i))&&(l=w(l,s))}A(l)}}var gn=!0;function bn(e,t){_n(t,e,!1)}function _n(e,t,n){switch(hn(t)){case 0:var r=function(e,t,n){se||le();var r=Tn,o=se;se=!0;try{ae(r,e,t,n)}finally{(se=o)||fe()}}.bind(null,t,1);break;case 1:r=function(e,t,n){Tn(e,t,n)}.bind(null,t,1);break;default:r=Tn.bind(null,t,1)}n?e.addEventListener(t,r,!0):e.addEventListener(t,r,!1)}function En(e,t,n,r){if(yn.length){var o=yn.pop();o.topLevelType=e,o.eventSystemFlags=t,o.nativeEvent=n,o.targetInst=r,e=o}else e={topLevelType:e,eventSystemFlags:t,nativeEvent:n,targetInst:r,ancestors:[]};try{if(t=vn,n=e,ce)t(n,void 0);else{ce=!0;try{ue(t,n,void 0)}finally{ce=!1,fe()}}}finally{e.topLevelType=null,e.nativeEvent=null,e.targetInst=null,e.ancestors.length=0,yn.length<mn&&yn.push(e)}}function Tn(e,t,n){if(gn)if(0<et.length&&-1<at.indexOf(e))e=ut(null,e,t,n),et.push(e);else{var r=Cn(e,t,n);null===r?st(e,n):-1<at.indexOf(e)?(e=ut(r,e,t,n),et.push(e)):function(e,t,n,r){switch(t){case\"focus\":return tt=ct(tt,e,t,n,r),!0;case\"dragenter\":return nt=ct(nt,e,t,n,r),!0;case\"mouseover\":return rt=ct(rt,e,t,n,r),!0;case\"pointerover\":var o=r.pointerId;return ot.set(o,ct(ot.get(o)||null,e,t,n,r)),!0;case\"gotpointercapture\":return o=r.pointerId,it.set(o,ct(it.get(o)||null,e,t,n,r)),!0}return!1}(r,e,t,n)||(st(e,n),En(e,t,n,null))}}function Cn(e,t,n){var r=Tt(n),o=or(r);if(null!==o)if(null===(r=bt(o)))o=null;else{var i=r.tag;if(13===i){if(null!==(r=13!==r.tag||(null===(o=r.memoizedState)&&(null!==(r=r.alternate)&&(o=r.memoizedState)),null===o)?null:o.dehydrated))return r;o=null}else if(3===i){if(r.stateNode.hydrate)return 3===r.tag?r.stateNode.containerInfo:null;o=null}else r!==o&&(o=null)}return En(e,t,n,o),null}function On(e){if(!Z)return!1;var t=(e=\"on\"+e)in document;return t||((t=document.createElement(\"div\")).setAttribute(e,\"return;\"),t=\"function\"===typeof t[e]),t}var wn=new(\"function\"===typeof WeakMap?WeakMap:Map);function Sn(e){var t=wn.get(e);return void 0===t&&(t=new Set,wn.set(e,t)),t}function xn(e,t,n){if(!n.has(e)){switch(e){case\"scroll\":_n(t,\"scroll\",!0);break;case\"focus\":case\"blur\":_n(t,\"focus\",!0),_n(t,\"blur\",!0),n.add(\"blur\"),n.add(\"focus\");break;case\"cancel\":case\"close\":On(e)&&_n(t,e,!0);break;case\"invalid\":case\"submit\":case\"reset\":break;default:-1===Ze.indexOf(e)&&bn(e,t)}n.add(e)}}var In={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},An=[\"Webkit\",\"ms\",\"Moz\",\"O\"];function kn(e,t,n){return null==t||\"boolean\"===typeof t||\"\"===t?\"\":n||\"number\"!==typeof t||0===t||In.hasOwnProperty(e)&&In[e]?(\"\"+t).trim():t+\"px\"}function Pn(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf(\"--\"),o=kn(n,t[n],r);\"float\"===n&&(n=\"cssFloat\"),r?e.setProperty(n,o):e[n]=o}}Object.keys(In).forEach(function(e){An.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),In[t]=In[e]})});var Nn=o({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ln(e,t){if(t){if(Nn[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw a(Error(137),e,\"\");if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw a(Error(60));if(!(\"object\"===typeof t.dangerouslySetInnerHTML&&\"__html\"in t.dangerouslySetInnerHTML))throw a(Error(61))}if(null!=t.style&&\"object\"!==typeof t.style)throw a(Error(62),\"\")}}function Rn(e,t){if(-1===e.indexOf(\"-\"))return\"string\"===typeof t.is;switch(e){case\"annotation-xml\":case\"color-profile\":case\"font-face\":case\"font-face-src\":case\"font-face-uri\":case\"font-face-format\":case\"font-face-name\":case\"missing-glyph\":return!1;default:return!0}}function Dn(e,t){var n=Sn(e=9===e.nodeType||11===e.nodeType?e:e.ownerDocument);t=h[t];for(var r=0;r<t.length;r++)xn(t[r],e,n)}function Mn(){}function jn(e){if(\"undefined\"===typeof(e=e||(\"undefined\"!==typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}function Fn(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function Un(e,t){var n,r=Fn(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=Fn(r)}}function Bn(){for(var e=window,t=jn();t instanceof e.HTMLIFrameElement;){try{var n=\"string\"===typeof t.contentWindow.location.href}catch(r){n=!1}if(!n)break;t=jn((e=t.contentWindow).document)}return t}function Vn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(\"input\"===t&&(\"text\"===e.type||\"search\"===e.type||\"tel\"===e.type||\"url\"===e.type||\"password\"===e.type)||\"textarea\"===t||\"true\"===e.contentEditable)}var Hn=\"$\",zn=\"/$\",Kn=\"$?\",Wn=\"$!\",Gn=null,qn=null;function Xn(e,t){switch(e){case\"button\":case\"input\":case\"select\":case\"textarea\":return!!t.autoFocus}return!1}function Yn(e,t){return\"textarea\"===e||\"option\"===e||\"noscript\"===e||\"string\"===typeof t.children||\"number\"===typeof t.children||\"object\"===typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var Qn=\"function\"===typeof setTimeout?setTimeout:void 0,$n=\"function\"===typeof clearTimeout?clearTimeout:void 0;function Zn(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break}return e}function Jn(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if(n===Hn||n===Wn||n===Kn){if(0===t)return e;t--}else n===zn&&t++}e=e.previousSibling}return null}var er=Math.random().toString(36).slice(2),tr=\"__reactInternalInstance$\"+er,nr=\"__reactEventHandlers$\"+er,rr=\"__reactContainere$\"+er;function or(e){var t=e[tr];if(t)return t;for(var n=e.parentNode;n;){if(t=n[rr]||n[tr]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=Jn(e);null!==e;){if(n=e[tr])return n;e=Jn(e)}return t}n=(e=n).parentNode}return null}function ir(e){return!(e=e[tr]||e[rr])||5!==e.tag&&6!==e.tag&&13!==e.tag&&3!==e.tag?null:e}function ar(e){if(5===e.tag||6===e.tag)return e.stateNode;throw a(Error(33))}function lr(e){return e[nr]||null}var ur=null,sr=null,cr=null;function fr(){if(cr)return cr;var e,t,n=sr,r=n.length,o=\"value\"in ur?ur.value:ur.textContent,i=o.length;for(e=0;e<r&&n[e]===o[e];e++);var a=r-e;for(t=1;t<=a&&n[r-t]===o[i-t];t++);return cr=o.slice(e,1<t?1-t:void 0)}var dr=Pt.extend({data:null}),pr=Pt.extend({data:null}),hr=[9,13,27,32],mr=Z&&\"CompositionEvent\"in window,yr=null;Z&&\"documentMode\"in document&&(yr=document.documentMode);var vr=Z&&\"TextEvent\"in window&&!yr,gr=Z&&(!mr||yr&&8<yr&&11>=yr),br=String.fromCharCode(32),_r={beforeInput:{phasedRegistrationNames:{bubbled:\"onBeforeInput\",captured:\"onBeforeInputCapture\"},dependencies:[\"compositionend\",\"keypress\",\"textInput\",\"paste\"]},compositionEnd:{phasedRegistrationNames:{bubbled:\"onCompositionEnd\",captured:\"onCompositionEndCapture\"},dependencies:\"blur compositionend keydown keypress keyup mousedown\".split(\" \")},compositionStart:{phasedRegistrationNames:{bubbled:\"onCompositionStart\",captured:\"onCompositionStartCapture\"},dependencies:\"blur compositionstart keydown keypress keyup mousedown\".split(\" \")},compositionUpdate:{phasedRegistrationNames:{bubbled:\"onCompositionUpdate\",captured:\"onCompositionUpdateCapture\"},dependencies:\"blur compositionupdate keydown keypress keyup mousedown\".split(\" \")}},Er=!1;function Tr(e,t){switch(e){case\"keyup\":return-1!==hr.indexOf(t.keyCode);case\"keydown\":return 229!==t.keyCode;case\"keypress\":case\"mousedown\":case\"blur\":return!0;default:return!1}}function Cr(e){return\"object\"===typeof(e=e.detail)&&\"data\"in e?e.data:null}var Or=!1;var wr={eventTypes:_r,extractEvents:function(e,t,n,r,o){var i;if(mr)e:{switch(e){case\"compositionstart\":var a=_r.compositionStart;break e;case\"compositionend\":a=_r.compositionEnd;break e;case\"compositionupdate\":a=_r.compositionUpdate;break e}a=void 0}else Or?Tr(e,r)&&(a=_r.compositionEnd):\"keydown\"===e&&229===r.keyCode&&(a=_r.compositionStart);return a?(gr&&\"ko\"!==r.locale&&(Or||a!==_r.compositionStart?a===_r.compositionEnd&&Or&&(i=fr()):(sr=\"value\"in(ur=o)?ur.value:ur.textContent,Or=!0)),t=dr.getPooled(a,n,r,o),i?t.data=i:null!==(i=Cr(r))&&(t.data=i),It(t),i=t):i=null,(e=vr?function(e,t){switch(e){case\"compositionend\":return Cr(t);case\"keypress\":return 32!==t.which?null:(Er=!0,br);case\"textInput\":return(e=t.data)===br&&Er?null:e;default:return null}}(e,r):function(e,t){if(Or)return\"compositionend\"===e||!mr&&Tr(e,t)?(e=fr(),cr=sr=ur=null,Or=!1,e):null;switch(e){case\"paste\":return null;case\"keypress\":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case\"compositionend\":return gr&&\"ko\"!==t.locale?null:t.data;default:return null}}(e,r))?((n=pr.getPooled(_r.beforeInput,n,r,o)).data=e,It(n)):n=null,null===i?n:null===n?i:[i,n]}},Sr={color:!0,date:!0,datetime:!0,\"datetime-local\":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function xr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return\"input\"===t?!!Sr[e.type]:\"textarea\"===t}var Ir={change:{phasedRegistrationNames:{bubbled:\"onChange\",captured:\"onChangeCapture\"},dependencies:\"blur change click focus input keydown keyup selectionchange\".split(\" \")}};function Ar(e,t,n){return(e=Pt.getPooled(Ir.change,e,t,n)).type=\"change\",re(n),It(e),e}var kr=null,Pr=null;function Nr(e){A(e)}function Lr(e){if(Oe(ar(e)))return e}function Rr(e,t){if(\"change\"===e)return t}var Dr=!1;function Mr(){kr&&(kr.detachEvent(\"onpropertychange\",jr),Pr=kr=null)}function jr(e){if(\"value\"===e.propertyName&&Lr(Pr))if(e=Ar(Pr,e,Tt(e)),se)A(e);else{se=!0;try{ie(Nr,e)}finally{se=!1,fe()}}}function Fr(e,t,n){\"focus\"===e?(Mr(),Pr=n,(kr=t).attachEvent(\"onpropertychange\",jr)):\"blur\"===e&&Mr()}function Ur(e){if(\"selectionchange\"===e||\"keyup\"===e||\"keydown\"===e)return Lr(Pr)}function Br(e,t){if(\"click\"===e)return Lr(t)}function Vr(e,t){if(\"input\"===e||\"change\"===e)return Lr(t)}Z&&(Dr=On(\"input\")&&(!document.documentMode||9<document.documentMode));var Hr={eventTypes:Ir,_isInputEventSupported:Dr,extractEvents:function(e,t,n,r,o){var i=(t=n?ar(n):window).nodeName&&t.nodeName.toLowerCase();if(\"select\"===i||\"input\"===i&&\"file\"===t.type)var a=Rr;else if(xr(t))if(Dr)a=Vr;else{a=Ur;var l=Fr}else(i=t.nodeName)&&\"input\"===i.toLowerCase()&&(\"checkbox\"===t.type||\"radio\"===t.type)&&(a=Br);if(a&&(a=a(e,n)))return Ar(a,r,o);l&&l(e,t,n),\"blur\"===e&&(e=t._wrapperState)&&e.controlled&&\"number\"===t.type&&ke(t,\"number\",t.value)}},zr={mouseEnter:{registrationName:\"onMouseEnter\",dependencies:[\"mouseout\",\"mouseover\"]},mouseLeave:{registrationName:\"onMouseLeave\",dependencies:[\"mouseout\",\"mouseover\"]},pointerEnter:{registrationName:\"onPointerEnter\",dependencies:[\"pointerout\",\"pointerover\"]},pointerLeave:{registrationName:\"onPointerLeave\",dependencies:[\"pointerout\",\"pointerover\"]}},Kr={eventTypes:zr,extractEvents:function(e,t,n,r,o){var i=\"mouseover\"===e||\"pointerover\"===e,a=\"mouseout\"===e||\"pointerout\"===e;if(i&&0===(32&t)&&(r.relatedTarget||r.fromElement)||!a&&!i)return null;if(t=o.window===o?o:(t=o.ownerDocument)?t.defaultView||t.parentWindow:window,a?(a=n,null!==(n=(n=r.relatedTarget||r.toElement)?or(n):null)&&(n!==(i=bt(n))||5!==n.tag&&6!==n.tag)&&(n=null)):a=null,a===n)return null;if(\"mouseout\"===e||\"mouseover\"===e)var l=Qt,u=zr.mouseLeave,s=zr.mouseEnter,c=\"mouse\";else\"pointerout\"!==e&&\"pointerover\"!==e||(l=$t,u=zr.pointerLeave,s=zr.pointerEnter,c=\"pointer\");if(e=null==a?t:ar(a),t=null==n?t:ar(n),(u=l.getPooled(u,a,r,o)).type=c+\"leave\",u.target=e,u.relatedTarget=t,(r=l.getPooled(s,n,r,o)).type=c+\"enter\",r.target=t,r.relatedTarget=e,c=n,(o=a)&&c)e:{for(s=c,e=0,a=l=o;a;a=Ct(a))e++;for(a=0,n=s;n;n=Ct(n))a++;for(;0<e-a;)l=Ct(l),e--;for(;0<a-e;)s=Ct(s),a--;for(;e--;){if(l===s||l===s.alternate)break e;l=Ct(l),s=Ct(s)}l=null}else l=null;for(s=l,l=[];o&&o!==s&&(null===(e=o.alternate)||e!==s);)l.push(o),o=Ct(o);for(o=[];c&&c!==s&&(null===(e=c.alternate)||e!==s);)o.push(c),c=Ct(c);for(c=0;c<l.length;c++)St(l[c],\"bubbled\",u);for(c=o.length;0<c--;)St(o[c],\"captured\",r);return[u,r]}};var Wr=\"function\"===typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e===1/t)||e!==e&&t!==t},Gr=Object.prototype.hasOwnProperty;function qr(e,t){if(Wr(e,t))return!0;if(\"object\"!==typeof e||null===e||\"object\"!==typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++)if(!Gr.call(t,n[r])||!Wr(e[n[r]],t[n[r]]))return!1;return!0}var Xr=Z&&\"documentMode\"in document&&11>=document.documentMode,Yr={select:{phasedRegistrationNames:{bubbled:\"onSelect\",captured:\"onSelectCapture\"},dependencies:\"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange\".split(\" \")}},Qr=null,$r=null,Zr=null,Jr=!1;function eo(e,t){var n=t.window===t?t.document:9===t.nodeType?t:t.ownerDocument;return Jr||null==Qr||Qr!==jn(n)?null:(\"selectionStart\"in(n=Qr)&&Vn(n)?n={start:n.selectionStart,end:n.selectionEnd}:n={anchorNode:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset},Zr&&qr(Zr,n)?null:(Zr=n,(e=Pt.getPooled(Yr.select,$r,e,t)).type=\"select\",e.target=Qr,It(e),e))}var to={eventTypes:Yr,extractEvents:function(e,t,n,r,o){var i;if(!(i=!(t=o.window===o?o.document:9===o.nodeType?o:o.ownerDocument))){e:{t=Sn(t),i=h.onSelect;for(var a=0;a<i.length;a++)if(!t.has(i[a])){t=!1;break e}t=!0}i=!t}if(i)return null;switch(t=n?ar(n):window,e){case\"focus\":(xr(t)||\"true\"===t.contentEditable)&&(Qr=t,$r=n,Zr=null);break;case\"blur\":Zr=$r=Qr=null;break;case\"mousedown\":Jr=!0;break;case\"contextmenu\":case\"mouseup\":case\"dragend\":return Jr=!1,eo(r,o);case\"selectionchange\":if(Xr)break;case\"keydown\":case\"keyup\":return eo(r,o)}return null}};k.injectEventPluginOrder(\"ResponderEventPlugin SimpleEventPlugin EnterLeaveEventPlugin ChangeEventPlugin SelectEventPlugin BeforeInputEventPlugin\".split(\" \")),E=lr,T=ir,C=ar,k.injectEventPluginsByName({SimpleEventPlugin:pn,EnterLeaveEventPlugin:Kr,ChangeEventPlugin:Hr,SelectEventPlugin:to,BeforeInputEventPlugin:wr}),new Set;var no=[],ro=-1;function oo(e){0>ro||(e.current=no[ro],no[ro]=null,ro--)}function io(e,t){no[++ro]=e.current,e.current=t}var ao={},lo={current:ao},uo={current:!1},so=ao;function co(e,t){var n=e.type.contextTypes;if(!n)return ao;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o,i={};for(o in n)i[o]=t[o];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function fo(e){return null!==(e=e.childContextTypes)&&void 0!==e}function po(e){oo(uo),oo(lo)}function ho(e){oo(uo),oo(lo)}function mo(e,t,n){if(lo.current!==ao)throw a(Error(168));io(lo,t),io(uo,n)}function yo(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,\"function\"!==typeof r.getChildContext)return n;for(var i in r=r.getChildContext())if(!(i in e))throw a(Error(108),Q(t)||\"Unknown\",i);return o({},n,{},r)}function vo(e){var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||ao,so=lo.current,io(lo,t),io(uo,uo.current),!0}function go(e,t,n){var r=e.stateNode;if(!r)throw a(Error(169));n?(t=yo(e,t,so),r.__reactInternalMemoizedMergedChildContext=t,oo(uo),oo(lo),io(lo,t)):oo(uo),io(uo,n)}var bo=i.unstable_runWithPriority,_o=i.unstable_scheduleCallback,Eo=i.unstable_cancelCallback,To=i.unstable_shouldYield,Co=i.unstable_requestPaint,Oo=i.unstable_now,wo=i.unstable_getCurrentPriorityLevel,So=i.unstable_ImmediatePriority,xo=i.unstable_UserBlockingPriority,Io=i.unstable_NormalPriority,Ao=i.unstable_LowPriority,ko=i.unstable_IdlePriority,Po={},No=void 0!==Co?Co:function(){},Lo=null,Ro=null,Do=!1,Mo=Oo(),jo=1e4>Mo?Oo:function(){return Oo()-Mo};function Fo(){switch(wo()){case So:return 99;case xo:return 98;case Io:return 97;case Ao:return 96;case ko:return 95;default:throw a(Error(332))}}function Uo(e){switch(e){case 99:return So;case 98:return xo;case 97:return Io;case 96:return Ao;case 95:return ko;default:throw a(Error(332))}}function Bo(e,t){return e=Uo(e),bo(e,t)}function Vo(e,t,n){return e=Uo(e),_o(e,t,n)}function Ho(e){return null===Lo?(Lo=[e],Ro=_o(So,Ko)):Lo.push(e),Po}function zo(){if(null!==Ro){var e=Ro;Ro=null,Eo(e)}Ko()}function Ko(){if(!Do&&null!==Lo){Do=!0;var e=0;try{var t=Lo;Bo(99,function(){for(;e<t.length;e++){var n=t[e];do{n=n(!0)}while(null!==n)}}),Lo=null}catch(n){throw null!==Lo&&(Lo=Lo.slice(e+1)),_o(So,zo),n}finally{Do=!1}}}function Wo(e,t){if(e&&e.defaultProps)for(var n in t=o({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}var Go={current:null},qo=null,Xo=null,Yo=null;function Qo(){Yo=Xo=qo=null}function $o(e,t){var n=e.type._context;io(Go,n._currentValue),n._currentValue=t}function Zo(e){var t=Go.current;oo(Go),e.type._context._currentValue=t}function Jo(e,t){for(;null!==e;){var n=e.alternate;if(e.childExpirationTime<t)e.childExpirationTime=t,null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t);else{if(!(null!==n&&n.childExpirationTime<t))break;n.childExpirationTime=t}e=e.return}}function ei(e,t){qo=e,Yo=Xo=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(e.expirationTime>=t&&(xa=!0),e.firstContext=null)}function ti(e,t){if(Yo!==e&&!1!==t&&0!==t)if(\"number\"===typeof t&&1073741823!==t||(Yo=e,t=1073741823),t={context:e,observedBits:t,next:null},null===Xo){if(null===qo)throw a(Error(308));Xo=t,qo.dependencies={expirationTime:0,firstContext:t,responders:null}}else Xo=Xo.next=t;return e._currentValue}var ni=!1;function ri(e){return{baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function oi(e){return{baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function ii(e,t){return{expirationTime:e,suspenseConfig:t,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function ai(e,t){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t)}function li(e,t){var n=e.alternate;if(null===n){var r=e.updateQueue,o=null;null===r&&(r=e.updateQueue=ri(e.memoizedState))}else r=e.updateQueue,o=n.updateQueue,null===r?null===o?(r=e.updateQueue=ri(e.memoizedState),o=n.updateQueue=ri(n.memoizedState)):r=e.updateQueue=oi(o):null===o&&(o=n.updateQueue=oi(r));null===o||r===o?ai(r,t):null===r.lastUpdate||null===o.lastUpdate?(ai(r,t),ai(o,t)):(ai(r,t),o.lastUpdate=t)}function ui(e,t){var n=e.updateQueue;null===(n=null===n?e.updateQueue=ri(e.memoizedState):si(e,n)).lastCapturedUpdate?n.firstCapturedUpdate=n.lastCapturedUpdate=t:(n.lastCapturedUpdate.next=t,n.lastCapturedUpdate=t)}function si(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=oi(t)),t}function ci(e,t,n,r,i,a){switch(n.tag){case 1:return\"function\"===typeof(e=n.payload)?e.call(a,r,i):e;case 3:e.effectTag=-4097&e.effectTag|64;case 0:if(null===(i=\"function\"===typeof(e=n.payload)?e.call(a,r,i):e)||void 0===i)break;return o({},r,i);case 2:ni=!0}return r}function fi(e,t,n,r,o){ni=!1;for(var i=(t=si(e,t)).baseState,a=null,l=0,u=t.firstUpdate,s=i;null!==u;){var c=u.expirationTime;c<o?(null===a&&(a=u,i=s),l<c&&(l=c)):(du(c,u.suspenseConfig),s=ci(e,0,u,s,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastEffect?t.firstEffect=t.lastEffect=u:(t.lastEffect.nextEffect=u,t.lastEffect=u))),u=u.next}for(c=null,u=t.firstCapturedUpdate;null!==u;){var f=u.expirationTime;f<o?(null===c&&(c=u,null===a&&(i=s)),l<f&&(l=f)):(s=ci(e,0,u,s,n,r),null!==u.callback&&(e.effectTag|=32,u.nextEffect=null,null===t.lastCapturedEffect?t.firstCapturedEffect=t.lastCapturedEffect=u:(t.lastCapturedEffect.nextEffect=u,t.lastCapturedEffect=u))),u=u.next}null===a&&(t.lastUpdate=null),null===c?t.lastCapturedUpdate=null:e.effectTag|=32,null===a&&null===c&&(i=s),t.baseState=i,t.firstUpdate=a,t.firstCapturedUpdate=c,pu(l),e.expirationTime=l,e.memoizedState=s}function di(e,t,n){null!==t.firstCapturedUpdate&&(null!==t.lastUpdate&&(t.lastUpdate.next=t.firstCapturedUpdate,t.lastUpdate=t.lastCapturedUpdate),t.firstCapturedUpdate=t.lastCapturedUpdate=null),pi(t.firstEffect,n),t.firstEffect=t.lastEffect=null,pi(t.firstCapturedEffect,n),t.firstCapturedEffect=t.lastCapturedEffect=null}function pi(e,t){for(;null!==e;){var n=e.callback;if(null!==n){e.callback=null;var r=t;if(\"function\"!==typeof n)throw a(Error(191),n);n.call(r)}e=e.nextEffect}}var hi=N.ReactCurrentBatchConfig,mi=(new r.Component).refs;function yi(e,t,n,r){n=null===(n=n(r,t=e.memoizedState))||void 0===n?t:o({},t,n),e.memoizedState=n,null!==(r=e.updateQueue)&&0===e.expirationTime&&(r.baseState=n)}var vi={isMounted:function(e){return!!(e=e._reactInternalFiber)&&bt(e)===e},enqueueSetState:function(e,t,n){e=e._reactInternalFiber;var r=Yl(),o=hi.suspense;(o=ii(r=Ql(r,e,o),o)).payload=t,void 0!==n&&null!==n&&(o.callback=n),li(e,o),Jl(e,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternalFiber;var r=Yl(),o=hi.suspense;(o=ii(r=Ql(r,e,o),o)).tag=1,o.payload=t,void 0!==n&&null!==n&&(o.callback=n),li(e,o),Jl(e,r)},enqueueForceUpdate:function(e,t){e=e._reactInternalFiber;var n=Yl(),r=hi.suspense;(r=ii(n=Ql(n,e,r),r)).tag=2,void 0!==t&&null!==t&&(r.callback=t),li(e,r),Jl(e,n)}};function gi(e,t,n,r,o,i,a){return\"function\"===typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,i,a):!t.prototype||!t.prototype.isPureReactComponent||(!qr(n,r)||!qr(o,i))}function bi(e,t,n){var r=!1,o=ao,i=t.contextType;return\"object\"===typeof i&&null!==i?i=ti(i):(o=fo(t)?so:lo.current,i=(r=null!==(r=t.contextTypes)&&void 0!==r)?co(e,o):ao),t=new t(n,i),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=vi,e.stateNode=t,t._reactInternalFiber=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=o,e.__reactInternalMemoizedMaskedChildContext=i),t}function _i(e,t,n,r){e=t.state,\"function\"===typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),\"function\"===typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&vi.enqueueReplaceState(t,t.state,null)}function Ei(e,t,n,r){var o=e.stateNode;o.props=n,o.state=e.memoizedState,o.refs=mi;var i=t.contextType;\"object\"===typeof i&&null!==i?o.context=ti(i):(i=fo(t)?so:lo.current,o.context=co(e,i)),null!==(i=e.updateQueue)&&(fi(e,i,n,o,r),o.state=e.memoizedState),\"function\"===typeof(i=t.getDerivedStateFromProps)&&(yi(e,t,i,n),o.state=e.memoizedState),\"function\"===typeof t.getDerivedStateFromProps||\"function\"===typeof o.getSnapshotBeforeUpdate||\"function\"!==typeof o.UNSAFE_componentWillMount&&\"function\"!==typeof o.componentWillMount||(t=o.state,\"function\"===typeof o.componentWillMount&&o.componentWillMount(),\"function\"===typeof o.UNSAFE_componentWillMount&&o.UNSAFE_componentWillMount(),t!==o.state&&vi.enqueueReplaceState(o,o.state,null),null!==(i=e.updateQueue)&&(fi(e,i,n,o,r),o.state=e.memoizedState)),\"function\"===typeof o.componentDidMount&&(e.effectTag|=4)}var Ti=Array.isArray;function Ci(e,t,n){if(null!==(e=n.ref)&&\"function\"!==typeof e&&\"object\"!==typeof e){if(n._owner){if(n=n._owner){if(1!==n.tag)throw a(Error(309));var r=n.stateNode}if(!r)throw a(Error(147),e);var o=\"\"+e;return null!==t&&null!==t.ref&&\"function\"===typeof t.ref&&t.ref._stringRef===o?t.ref:((t=function(e){var t=r.refs;t===mi&&(t=r.refs={}),null===e?delete t[o]:t[o]=e})._stringRef=o,t)}if(\"string\"!==typeof e)throw a(Error(284));if(!n._owner)throw a(Error(290),e)}return e}function Oi(e,t){if(\"textarea\"!==e.type)throw a(Error(31),\"[object Object]\"===Object.prototype.toString.call(t)?\"object with keys {\"+Object.keys(t).join(\", \")+\"}\":t,\"\")}function wi(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.effectTag=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function o(e,t,n){return(e=Pu(e,t)).index=0,e.sibling=null,e}function i(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.effectTag=vt,n):r:(t.effectTag=vt,n):n}function l(t){return e&&null===t.alternate&&(t.effectTag=vt),t}function u(e,t,n,r){return null===t||6!==t.tag?((t=Ru(n,e.mode,r)).return=e,t):((t=o(t,n)).return=e,t)}function s(e,t,n,r){return null!==t&&t.elementType===n.type?((r=o(t,n.props)).ref=Ci(e,t,n),r.return=e,r):((r=Nu(n.type,n.key,n.props,null,e.mode,r)).ref=Ci(e,t,n),r.return=e,r)}function c(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=Du(n,e.mode,r)).return=e,t):((t=o(t,n.children||[])).return=e,t)}function f(e,t,n,r,i){return null===t||7!==t.tag?((t=Lu(n,e.mode,r,i)).return=e,t):((t=o(t,n)).return=e,t)}function d(e,t,n){if(\"string\"===typeof t||\"number\"===typeof t)return(t=Ru(\"\"+t,e.mode,n)).return=e,t;if(\"object\"===typeof t&&null!==t){switch(t.$$typeof){case D:return(n=Nu(t.type,t.key,t.props,null,e.mode,n)).ref=Ci(e,null,t),n.return=e,n;case M:return(t=Du(t,e.mode,n)).return=e,t}if(Ti(t)||Y(t))return(t=Lu(t,e.mode,n,null)).return=e,t;Oi(e,t)}return null}function p(e,t,n,r){var o=null!==t?t.key:null;if(\"string\"===typeof n||\"number\"===typeof n)return null!==o?null:u(e,t,\"\"+n,r);if(\"object\"===typeof n&&null!==n){switch(n.$$typeof){case D:return n.key===o?n.type===j?f(e,t,n.props.children,r,o):s(e,t,n,r):null;case M:return n.key===o?c(e,t,n,r):null}if(Ti(n)||Y(n))return null!==o?null:f(e,t,n,r,null);Oi(e,n)}return null}function h(e,t,n,r,o){if(\"string\"===typeof r||\"number\"===typeof r)return u(t,e=e.get(n)||null,\"\"+r,o);if(\"object\"===typeof r&&null!==r){switch(r.$$typeof){case D:return e=e.get(null===r.key?n:r.key)||null,r.type===j?f(t,e,r.props.children,o,r.key):s(t,e,r,o);case M:return c(t,e=e.get(null===r.key?n:r.key)||null,r,o)}if(Ti(r)||Y(r))return f(t,e=e.get(n)||null,r,o,null);Oi(t,r)}return null}function m(o,a,l,u){for(var s=null,c=null,f=a,m=a=0,y=null;null!==f&&m<l.length;m++){f.index>m?(y=f,f=null):y=f.sibling;var v=p(o,f,l[m],u);if(null===v){null===f&&(f=y);break}e&&f&&null===v.alternate&&t(o,f),a=i(v,a,m),null===c?s=v:c.sibling=v,c=v,f=y}if(m===l.length)return n(o,f),s;if(null===f){for(;m<l.length;m++)null!==(f=d(o,l[m],u))&&(a=i(f,a,m),null===c?s=f:c.sibling=f,c=f);return s}for(f=r(o,f);m<l.length;m++)null!==(y=h(f,o,m,l[m],u))&&(e&&null!==y.alternate&&f.delete(null===y.key?m:y.key),a=i(y,a,m),null===c?s=y:c.sibling=y,c=y);return e&&f.forEach(function(e){return t(o,e)}),s}function y(o,l,u,s){var c=Y(u);if(\"function\"!==typeof c)throw a(Error(150));if(null==(u=c.call(u)))throw a(Error(151));for(var f=c=null,m=l,y=l=0,v=null,g=u.next();null!==m&&!g.done;y++,g=u.next()){m.index>y?(v=m,m=null):v=m.sibling;var b=p(o,m,g.value,s);if(null===b){null===m&&(m=v);break}e&&m&&null===b.alternate&&t(o,m),l=i(b,l,y),null===f?c=b:f.sibling=b,f=b,m=v}if(g.done)return n(o,m),c;if(null===m){for(;!g.done;y++,g=u.next())null!==(g=d(o,g.value,s))&&(l=i(g,l,y),null===f?c=g:f.sibling=g,f=g);return c}for(m=r(o,m);!g.done;y++,g=u.next())null!==(g=h(m,o,y,g.value,s))&&(e&&null!==g.alternate&&m.delete(null===g.key?y:g.key),l=i(g,l,y),null===f?c=g:f.sibling=g,f=g);return e&&m.forEach(function(e){return t(o,e)}),c}return function(e,r,i,u){var s=\"object\"===typeof i&&null!==i&&i.type===j&&null===i.key;s&&(i=i.props.children);var c=\"object\"===typeof i&&null!==i;if(c)switch(i.$$typeof){case D:e:{for(c=i.key,s=r;null!==s;){if(s.key===c){if(7===s.tag?i.type===j:s.elementType===i.type){n(e,s.sibling),(r=o(s,i.type===j?i.props.children:i.props)).ref=Ci(e,s,i),r.return=e,e=r;break e}n(e,s);break}t(e,s),s=s.sibling}i.type===j?((r=Lu(i.props.children,e.mode,u,i.key)).return=e,e=r):((u=Nu(i.type,i.key,i.props,null,e.mode,u)).ref=Ci(e,r,i),u.return=e,e=u)}return l(e);case M:e:{for(s=i.key;null!==r;){if(r.key===s){if(4===r.tag&&r.stateNode.containerInfo===i.containerInfo&&r.stateNode.implementation===i.implementation){n(e,r.sibling),(r=o(r,i.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=Du(i,e.mode,u)).return=e,e=r}return l(e)}if(\"string\"===typeof i||\"number\"===typeof i)return i=\"\"+i,null!==r&&6===r.tag?(n(e,r.sibling),(r=o(r,i)).return=e,e=r):(n(e,r),(r=Ru(i,e.mode,u)).return=e,e=r),l(e);if(Ti(i))return m(e,r,i,u);if(Y(i))return y(e,r,i,u);if(c&&Oi(e,i),\"undefined\"===typeof i&&!s)switch(e.tag){case 1:case 0:throw e=e.type,a(Error(152),e.displayName||e.name||\"Component\")}return n(e,r)}}var Si=wi(!0),xi=wi(!1),Ii={},Ai={current:Ii},ki={current:Ii},Pi={current:Ii};function Ni(e){if(e===Ii)throw a(Error(174));return e}function Li(e,t){io(Pi,t),io(ki,e),io(Ai,Ii);var n=t.nodeType;switch(n){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:Ue(null,\"\");break;default:t=Ue(t=(n=8===n?t.parentNode:t).namespaceURI||null,n=n.tagName)}oo(Ai),io(Ai,t)}function Ri(e){oo(Ai),oo(ki),oo(Pi)}function Di(e){Ni(Pi.current);var t=Ni(Ai.current),n=Ue(t,e.type);t!==n&&(io(ki,e),io(Ai,n))}function Mi(e){ki.current===e&&(oo(Ai),oo(ki))}var ji={current:0};function Fi(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||n.data===Kn||n.data===Wn))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if((64&t.effectTag)!==yt)return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function Ui(e,t){return{responder:e,props:t}}var Bi=N.ReactCurrentDispatcher,Vi=0,Hi=null,zi=null,Ki=null,Wi=null,Gi=null,qi=null,Xi=0,Yi=null,Qi=0,$i=!1,Zi=null,Ji=0;function ea(){throw a(Error(321))}function ta(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!Wr(e[n],t[n]))return!1;return!0}function na(e,t,n,r,o,i){if(Vi=i,Hi=t,Ki=null!==e?e.memoizedState:null,Bi.current=null===Ki?ma:ya,t=n(r,o),$i){do{$i=!1,Ji+=1,Ki=null!==e?e.memoizedState:null,qi=Wi,Yi=Gi=zi=null,Bi.current=ya,t=n(r,o)}while($i);Zi=null,Ji=0}if(Bi.current=ha,(e=Hi).memoizedState=Wi,e.expirationTime=Xi,e.updateQueue=Yi,e.effectTag|=Qi,e=null!==zi&&null!==zi.next,Vi=0,qi=Gi=Wi=Ki=zi=Hi=null,Xi=0,Yi=null,Qi=0,e)throw a(Error(300));return t}function ra(){Bi.current=ha,Vi=0,qi=Gi=Wi=Ki=zi=Hi=null,Xi=0,Yi=null,Qi=0,$i=!1,Zi=null,Ji=0}function oa(){var e={memoizedState:null,baseState:null,queue:null,baseUpdate:null,next:null};return null===Gi?Wi=Gi=e:Gi=Gi.next=e,Gi}function ia(){if(null!==qi)qi=(Gi=qi).next,Ki=null!==(zi=Ki)?zi.next:null;else{if(null===Ki)throw a(Error(310));var e={memoizedState:(zi=Ki).memoizedState,baseState:zi.baseState,queue:zi.queue,baseUpdate:zi.baseUpdate,next:null};Gi=null===Gi?Wi=e:Gi.next=e,Ki=zi.next}return Gi}function aa(e,t){return\"function\"===typeof t?t(e):t}function la(e){var t=ia(),n=t.queue;if(null===n)throw a(Error(311));if(n.lastRenderedReducer=e,0<Ji){var r=n.dispatch;if(null!==Zi){var o=Zi.get(n);if(void 0!==o){Zi.delete(n);var i=t.memoizedState;do{i=e(i,o.action),o=o.next}while(null!==o);return Wr(i,t.memoizedState)||(xa=!0),t.memoizedState=i,t.baseUpdate===n.last&&(t.baseState=i),n.lastRenderedState=i,[i,r]}}return[t.memoizedState,r]}r=n.last;var l=t.baseUpdate;if(i=t.baseState,null!==l?(null!==r&&(r.next=null),r=l.next):r=null!==r?r.next:null,null!==r){var u=o=null,s=r,c=!1;do{var f=s.expirationTime;f<Vi?(c||(c=!0,u=l,o=i),f>Xi&&pu(Xi=f)):(du(f,s.suspenseConfig),i=s.eagerReducer===e?s.eagerState:e(i,s.action)),l=s,s=s.next}while(null!==s&&s!==r);c||(u=l,o=i),Wr(i,t.memoizedState)||(xa=!0),t.memoizedState=i,t.baseUpdate=u,t.baseState=o,n.lastRenderedState=i}return[t.memoizedState,n.dispatch]}function ua(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===Yi?(Yi={lastEffect:null}).lastEffect=e.next=e:null===(t=Yi.lastEffect)?Yi.lastEffect=e.next=e:(n=t.next,t.next=e,e.next=n,Yi.lastEffect=e),e}function sa(e,t,n,r){var o=oa();Qi|=e,o.memoizedState=ua(t,n,void 0,void 0===r?null:r)}function ca(e,t,n,r){var o=ia();r=void 0===r?null:r;var i=void 0;if(null!==zi){var a=zi.memoizedState;if(i=a.destroy,null!==r&&ta(r,a.deps))return void ua(0,n,i,r)}Qi|=e,o.memoizedState=ua(t,n,i,r)}function fa(e,t){return\"function\"===typeof t?(e=e(),t(e),function(){t(null)}):null!==t&&void 0!==t?(e=e(),t.current=e,function(){t.current=null}):void 0}function da(){}function pa(e,t,n){if(!(25>Ji))throw a(Error(301));var r=e.alternate;if(e===Hi||null!==r&&r===Hi)if($i=!0,e={expirationTime:Vi,suspenseConfig:null,action:n,eagerReducer:null,eagerState:null,next:null},null===Zi&&(Zi=new Map),void 0===(n=Zi.get(t)))Zi.set(t,e);else{for(t=n;null!==t.next;)t=t.next;t.next=e}else{var o=Yl(),i=hi.suspense;i={expirationTime:o=Ql(o,e,i),suspenseConfig:i,action:n,eagerReducer:null,eagerState:null,next:null};var l=t.last;if(null===l)i.next=i;else{var u=l.next;null!==u&&(i.next=u),l.next=i}if(t.last=i,0===e.expirationTime&&(null===r||0===r.expirationTime)&&null!==(r=t.lastRenderedReducer))try{var s=t.lastRenderedState,c=r(s,n);if(i.eagerReducer=r,i.eagerState=c,Wr(c,s))return}catch(f){}Jl(e,o)}}var ha={readContext:ti,useCallback:ea,useContext:ea,useEffect:ea,useImperativeHandle:ea,useLayoutEffect:ea,useMemo:ea,useReducer:ea,useRef:ea,useState:ea,useDebugValue:ea,useResponder:ea},ma={readContext:ti,useCallback:function(e,t){return oa().memoizedState=[e,void 0===t?null:t],e},useContext:ti,useEffect:function(e,t){return sa(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,sa(4,36,fa.bind(null,t,e),n)},useLayoutEffect:function(e,t){return sa(4,36,e,t)},useMemo:function(e,t){var n=oa();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=oa();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={last:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=pa.bind(null,Hi,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},oa().memoizedState=e},useState:function(e){var t=oa();return\"function\"===typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={last:null,dispatch:null,lastRenderedReducer:aa,lastRenderedState:e}).dispatch=pa.bind(null,Hi,e),[t.memoizedState,e]},useDebugValue:da,useResponder:Ui},ya={readContext:ti,useCallback:function(e,t){var n=ia();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ta(t,r[1])?r[0]:(n.memoizedState=[e,t],e)},useContext:ti,useEffect:function(e,t){return ca(516,192,e,t)},useImperativeHandle:function(e,t,n){return n=null!==n&&void 0!==n?n.concat([e]):null,ca(4,36,fa.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ca(4,36,e,t)},useMemo:function(e,t){var n=ia();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ta(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)},useReducer:la,useRef:function(){return ia().memoizedState},useState:function(e){return la(aa)},useDebugValue:da,useResponder:Ui},va=null,ga=null,ba=!1;function _a(e,t){var n=Au(5,null,null,0);n.elementType=\"DELETED\",n.type=\"DELETED\",n.stateNode=t,n.return=e,n.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function Ea(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,!0);case 6:return null!==(t=\"\"===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,!0);case 13:default:return!1}}function Ta(e){if(ba){var t=ga;if(t){var n=t;if(!Ea(e,t)){if(!(t=Zn(n.nextSibling))||!Ea(e,t))return e.effectTag=e.effectTag&~gt|vt,ba=!1,void(va=e);_a(va,n)}va=e,ga=Zn(t.firstChild)}else e.effectTag=e.effectTag&~gt|vt,ba=!1,va=e}}function Ca(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;va=e}function Oa(e){if(e!==va)return!1;if(!ba)return Ca(e),ba=!0,!1;var t=e.type;if(5!==e.tag||\"head\"!==t&&\"body\"!==t&&!Yn(t,e.memoizedProps))for(t=ga;t;)_a(e,t),t=Zn(t.nextSibling);if(Ca(e),13===e.tag)if(null===(e=null!==(e=e.memoizedState)?e.dehydrated:null))e=ga;else e:{for(e=e.nextSibling,t=0;e;){if(8===e.nodeType){var n=e.data;if(n===zn){if(0===t){e=Zn(e.nextSibling);break e}t--}else n!==Hn&&n!==Wn&&n!==Kn||t++}e=e.nextSibling}e=null}else e=va?Zn(e.stateNode.nextSibling):null;return ga=e,!0}function wa(){ga=va=null,ba=!1}var Sa=N.ReactCurrentOwner,xa=!1;function Ia(e,t,n,r){t.child=null===e?xi(t,null,n,r):Si(t,e.child,n,r)}function Aa(e,t,n,r,o){n=n.render;var i=t.ref;return ei(t,o),r=na(e,t,n,r,i,o),null===e||xa?(t.effectTag|=1,Ia(e,t,r,o),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=o&&(e.expirationTime=0),Wa(e,t,o))}function ka(e,t,n,r,o,i){if(null===e){var a=n.type;return\"function\"!==typeof a||ku(a)||void 0!==a.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Nu(n.type,null,r,null,t.mode,i)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=a,Pa(e,t,a,r,o,i))}return a=e.child,o<i&&(o=a.memoizedProps,(n=null!==(n=n.compare)?n:qr)(o,r)&&e.ref===t.ref)?Wa(e,t,i):(t.effectTag|=1,(e=Pu(a,r)).ref=t.ref,e.return=t,t.child=e)}function Pa(e,t,n,r,o,i){return null!==e&&qr(e.memoizedProps,r)&&e.ref===t.ref&&(xa=!1,o<i)?Wa(e,t,i):La(e,t,n,r,i)}function Na(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.effectTag|=128)}function La(e,t,n,r,o){var i=fo(n)?so:lo.current;return i=co(t,i),ei(t,o),n=na(e,t,n,r,i,o),null===e||xa?(t.effectTag|=1,Ia(e,t,n,o),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=o&&(e.expirationTime=0),Wa(e,t,o))}function Ra(e,t,n,r,o){if(fo(n)){var i=!0;vo(t)}else i=!1;if(ei(t,o),null===t.stateNode)null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=vt),bi(t,n,r),Ei(t,n,r,o),r=!0;else if(null===e){var a=t.stateNode,l=t.memoizedProps;a.props=l;var u=a.context,s=n.contextType;\"object\"===typeof s&&null!==s?s=ti(s):s=co(t,s=fo(n)?so:lo.current);var c=n.getDerivedStateFromProps,f=\"function\"===typeof c||\"function\"===typeof a.getSnapshotBeforeUpdate;f||\"function\"!==typeof a.UNSAFE_componentWillReceiveProps&&\"function\"!==typeof a.componentWillReceiveProps||(l!==r||u!==s)&&_i(t,a,r,s),ni=!1;var d=t.memoizedState;u=a.state=d;var p=t.updateQueue;null!==p&&(fi(t,p,r,a,o),u=t.memoizedState),l!==r||d!==u||uo.current||ni?(\"function\"===typeof c&&(yi(t,n,c,r),u=t.memoizedState),(l=ni||gi(t,n,l,r,d,u,s))?(f||\"function\"!==typeof a.UNSAFE_componentWillMount&&\"function\"!==typeof a.componentWillMount||(\"function\"===typeof a.componentWillMount&&a.componentWillMount(),\"function\"===typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount()),\"function\"===typeof a.componentDidMount&&(t.effectTag|=4)):(\"function\"===typeof a.componentDidMount&&(t.effectTag|=4),t.memoizedProps=r,t.memoizedState=u),a.props=r,a.state=u,a.context=s,r=l):(\"function\"===typeof a.componentDidMount&&(t.effectTag|=4),r=!1)}else a=t.stateNode,l=t.memoizedProps,a.props=t.type===t.elementType?l:Wo(t.type,l),u=a.context,\"object\"===typeof(s=n.contextType)&&null!==s?s=ti(s):s=co(t,s=fo(n)?so:lo.current),(f=\"function\"===typeof(c=n.getDerivedStateFromProps)||\"function\"===typeof a.getSnapshotBeforeUpdate)||\"function\"!==typeof a.UNSAFE_componentWillReceiveProps&&\"function\"!==typeof a.componentWillReceiveProps||(l!==r||u!==s)&&_i(t,a,r,s),ni=!1,u=t.memoizedState,d=a.state=u,null!==(p=t.updateQueue)&&(fi(t,p,r,a,o),d=t.memoizedState),l!==r||u!==d||uo.current||ni?(\"function\"===typeof c&&(yi(t,n,c,r),d=t.memoizedState),(c=ni||gi(t,n,l,r,u,d,s))?(f||\"function\"!==typeof a.UNSAFE_componentWillUpdate&&\"function\"!==typeof a.componentWillUpdate||(\"function\"===typeof a.componentWillUpdate&&a.componentWillUpdate(r,d,s),\"function\"===typeof a.UNSAFE_componentWillUpdate&&a.UNSAFE_componentWillUpdate(r,d,s)),\"function\"===typeof a.componentDidUpdate&&(t.effectTag|=4),\"function\"===typeof a.getSnapshotBeforeUpdate&&(t.effectTag|=256)):(\"function\"!==typeof a.componentDidUpdate||l===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),\"function\"!==typeof a.getSnapshotBeforeUpdate||l===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),t.memoizedProps=r,t.memoizedState=d),a.props=r,a.state=d,a.context=s,r=c):(\"function\"!==typeof a.componentDidUpdate||l===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=4),\"function\"!==typeof a.getSnapshotBeforeUpdate||l===e.memoizedProps&&u===e.memoizedState||(t.effectTag|=256),r=!1);return Da(e,t,n,r,i,o)}function Da(e,t,n,r,o,i){Na(e,t);var a=(64&t.effectTag)!==yt;if(!r&&!a)return o&&go(t,n,!1),Wa(e,t,i);r=t.stateNode,Sa.current=t;var l=a&&\"function\"!==typeof n.getDerivedStateFromError?null:r.render();return t.effectTag|=1,null!==e&&a?(t.child=Si(t,e.child,null,i),t.child=Si(t,null,l,i)):Ia(e,t,l,i),t.memoizedState=r.state,o&&go(t,n,!0),t.child}function Ma(e){var t=e.stateNode;t.pendingContext?mo(0,t.pendingContext,t.pendingContext!==t.context):t.context&&mo(0,t.context,!1),Li(e,t.containerInfo)}var ja,Fa,Ua,Ba,Va={dehydrated:null,retryTime:1};function Ha(e,t,n){var r,o=t.mode,i=t.pendingProps,a=ji.current,l=!1;if((r=(64&t.effectTag)!==yt)||(r=0!==(2&a)&&(null===e||null!==e.memoizedState)),r?(l=!0,t.effectTag&=-65):null!==e&&null===e.memoizedState||void 0===i.fallback||!0===i.unstable_avoidThisFallback||(a|=1),io(ji,1&a),null===e){if(l){if(l=i.fallback,(i=Lu(null,o,0,null)).return=t,0===(2&t.mode))for(e=null!==t.memoizedState?t.child.child:t.child,i.child=e;null!==e;)e.return=i,e=e.sibling;return(n=Lu(l,o,n,null)).return=t,i.sibling=n,t.memoizedState=Va,t.child=i,n}return o=i.children,t.memoizedState=null,t.child=xi(t,null,o,n)}if(null!==e.memoizedState){if(o=(e=e.child).sibling,l){if(i=i.fallback,(n=Pu(e,e.pendingProps)).return=t,0===(2&t.mode)&&(l=null!==t.memoizedState?t.child.child:t.child)!==e.child)for(n.child=l;null!==l;)l.return=n,l=l.sibling;return(o=Pu(o,i,o.expirationTime)).return=t,n.sibling=o,n.childExpirationTime=0,t.memoizedState=Va,t.child=n,o}return n=Si(t,e.child,i.children,n),t.memoizedState=null,t.child=n}if(e=e.child,l){if(l=i.fallback,(i=Lu(null,o,0,null)).return=t,i.child=e,null!==e&&(e.return=i),0===(2&t.mode))for(e=null!==t.memoizedState?t.child.child:t.child,i.child=e;null!==e;)e.return=i,e=e.sibling;return(n=Lu(l,o,n,null)).return=t,i.sibling=n,n.effectTag|=vt,i.childExpirationTime=0,t.memoizedState=Va,t.child=i,n}return t.memoizedState=null,t.child=Si(t,e,i.children,n)}function za(e,t,n,r,o){var i=e.memoizedState;null===i?e.memoizedState={isBackwards:t,rendering:null,last:r,tail:n,tailExpiration:0,tailMode:o}:(i.isBackwards=t,i.rendering=null,i.last=r,i.tail=n,i.tailExpiration=0,i.tailMode=o)}function Ka(e,t,n){var r=t.pendingProps,o=r.revealOrder,i=r.tail;if(Ia(e,t,r.children,n),0!==(2&(r=ji.current)))r=1&r|2,t.effectTag|=64;else{if(null!==e&&(64&e.effectTag)!==yt)e:for(e=t.child;null!==e;){if(13===e.tag){if(null!==e.memoizedState){e.expirationTime<n&&(e.expirationTime=n);var a=e.alternate;null!==a&&a.expirationTime<n&&(a.expirationTime=n),Jo(e.return,n)}}else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(io(ji,r),0===(2&t.mode))t.memoizedState=null;else switch(o){case\"forwards\":for(n=t.child,o=null;null!==n;)null!==(r=n.alternate)&&null===Fi(r)&&(o=n),n=n.sibling;null===(n=o)?(o=t.child,t.child=null):(o=n.sibling,n.sibling=null),za(t,!1,o,n,i);break;case\"backwards\":for(n=null,o=t.child,t.child=null;null!==o;){if(null!==(r=o.alternate)&&null===Fi(r)){t.child=o;break}r=o.sibling,o.sibling=n,n=o,o=r}za(t,!0,n,null,i);break;case\"together\":za(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function Wa(e,t,n){null!==e&&(t.dependencies=e.dependencies);var r=t.expirationTime;if(0!==r&&pu(r),t.childExpirationTime<n)return null;if(null!==e&&t.child!==e.child)throw a(Error(153));if(null!==t.child){for(n=Pu(e=t.child,e.pendingProps,e.expirationTime),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Pu(e,e.pendingProps,e.expirationTime)).return=t;n.sibling=null}return t.child}function Ga(e){e.effectTag|=4}function qa(e,t){switch(e.tailMode){case\"hidden\":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case\"collapsed\":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Xa(e){switch(e.tag){case 1:fo(e.type)&&po();var t=e.effectTag;return 4096&t?(e.effectTag=-4097&t|64,e):null;case 3:if(Ri(),ho(),(64&(t=e.effectTag))!==yt)throw a(Error(285));return e.effectTag=-4097&t|64,e;case 5:return Mi(e),null;case 13:return oo(ji),4096&(t=e.effectTag)?(e.effectTag=-4097&t|64,e):null;case 19:return oo(ji),null;case 4:return Ri(),null;case 10:return Zo(e),null;default:return null}}function Ya(e,t){return{value:e,source:t,stack:$(t)}}ja=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},Fa=function(){},Ua=function(e,t,n,r,i){var a=e.memoizedProps;if(a!==r){var l,u,s=t.stateNode;switch(Ni(Ai.current),e=null,n){case\"input\":a=we(s,a),r=we(s,r),e=[];break;case\"option\":a=Pe(s,a),r=Pe(s,r),e=[];break;case\"select\":a=o({},a,{value:void 0}),r=o({},r,{value:void 0}),e=[];break;case\"textarea\":a=Le(s,a),r=Le(s,r),e=[];break;default:\"function\"!==typeof a.onClick&&\"function\"===typeof r.onClick&&(s.onclick=Mn)}for(l in Ln(n,r),n=null,a)if(!r.hasOwnProperty(l)&&a.hasOwnProperty(l)&&null!=a[l])if(\"style\"===l)for(u in s=a[l])s.hasOwnProperty(u)&&(n||(n={}),n[u]=\"\");else\"dangerouslySetInnerHTML\"!==l&&\"children\"!==l&&\"suppressContentEditableWarning\"!==l&&\"suppressHydrationWarning\"!==l&&\"autoFocus\"!==l&&(p.hasOwnProperty(l)?e||(e=[]):(e=e||[]).push(l,null));for(l in r){var c=r[l];if(s=null!=a?a[l]:void 0,r.hasOwnProperty(l)&&c!==s&&(null!=c||null!=s))if(\"style\"===l)if(s){for(u in s)!s.hasOwnProperty(u)||c&&c.hasOwnProperty(u)||(n||(n={}),n[u]=\"\");for(u in c)c.hasOwnProperty(u)&&s[u]!==c[u]&&(n||(n={}),n[u]=c[u])}else n||(e||(e=[]),e.push(l,n)),n=c;else\"dangerouslySetInnerHTML\"===l?(c=c?c.__html:void 0,s=s?s.__html:void 0,null!=c&&s!==c&&(e=e||[]).push(l,\"\"+c)):\"children\"===l?s===c||\"string\"!==typeof c&&\"number\"!==typeof c||(e=e||[]).push(l,\"\"+c):\"suppressContentEditableWarning\"!==l&&\"suppressHydrationWarning\"!==l&&(p.hasOwnProperty(l)?(null!=c&&Dn(i,l),e||s===c||(e=[])):(e=e||[]).push(l,c))}n&&(e=e||[]).push(\"style\",n),i=e,(t.updateQueue=i)&&Ga(t)}},Ba=function(e,t,n,r){n!==r&&Ga(t)};var Qa=\"function\"===typeof WeakSet?WeakSet:Set;function $a(e,t){var n=t.source,r=t.stack;null===r&&null!==n&&(r=$(n)),null!==n&&Q(n.type),t=t.value,null!==e&&1===e.tag&&Q(e.type);try{console.error(t)}catch(o){setTimeout(function(){throw o})}}function Za(e){var t=e.ref;if(null!==t)if(\"function\"===typeof t)try{t(null)}catch(n){Ou(e,n)}else t.current=null}function Ja(e,t){switch(t.tag){case 0:case 11:case 15:el(2,0,t);break;case 1:if(256&t.effectTag&&null!==e){var n=e.memoizedProps,r=e.memoizedState;t=(e=t.stateNode).getSnapshotBeforeUpdate(t.elementType===t.type?n:Wo(t.type,n),r),e.__reactInternalSnapshotBeforeUpdate=t}break;case 3:case 5:case 6:case 4:case 17:break;default:throw a(Error(163))}}function el(e,t,n){if(null!==(n=null!==(n=n.updateQueue)?n.lastEffect:null)){var r=n=n.next;do{if(0!==(r.tag&e)){var o=r.destroy;r.destroy=void 0,void 0!==o&&o()}0!==(r.tag&t)&&(o=r.create,r.destroy=o()),r=r.next}while(r!==n)}}function tl(e,t,n){switch(\"function\"===typeof xu&&xu(t),t.tag){case 0:case 11:case 14:case 15:if(null!==(e=t.updateQueue)&&null!==(e=e.lastEffect)){var r=e.next;Bo(97<n?97:n,function(){var e=r;do{var n=e.destroy;if(void 0!==n){var o=t;try{n()}catch(i){Ou(o,i)}}e=e.next}while(e!==r)})}break;case 1:Za(t),\"function\"===typeof(n=t.stateNode).componentWillUnmount&&function(e,t){try{t.props=e.memoizedProps,t.state=e.memoizedState,t.componentWillUnmount()}catch(n){Ou(e,n)}}(t,n);break;case 5:Za(t);break;case 4:il(e,t,n)}}function nl(e){var t=e.alternate;e.return=null,e.child=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null,e.alternate=null,e.firstEffect=null,e.lastEffect=null,e.pendingProps=null,e.memoizedProps=null,null!==t&&nl(t)}function rl(e){return 5===e.tag||3===e.tag||4===e.tag}function ol(e){e:{for(var t=e.return;null!==t;){if(rl(t)){var n=t;break e}t=t.return}throw a(Error(160))}switch(t=n.stateNode,n.tag){case 5:var r=!1;break;case 3:case 4:t=t.containerInfo,r=!0;break;default:throw a(Error(161))}16&n.effectTag&&(He(t,\"\"),n.effectTag&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||rl(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag&&18!==n.tag;){if(n.effectTag&vt)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(n.effectTag&vt)){n=n.stateNode;break e}}for(var o=e;;){var i=5===o.tag||6===o.tag;if(i){var l=i?o.stateNode:o.stateNode.instance;if(n)if(r){var u=l;l=n,8===(i=t).nodeType?i.parentNode.insertBefore(u,l):i.insertBefore(u,l)}else t.insertBefore(l,n);else r?(8===(u=t).nodeType?(i=u.parentNode).insertBefore(l,u):(i=u).appendChild(l),null!==(u=u._reactRootContainer)&&void 0!==u||null!==i.onclick||(i.onclick=Mn)):t.appendChild(l)}else if(4!==o.tag&&null!==o.child){o.child.return=o,o=o.child;continue}if(o===e)break;for(;null===o.sibling;){if(null===o.return||o.return===e)return;o=o.return}o.sibling.return=o.return,o=o.sibling}}function il(e,t,n){for(var r,o,i=t,l=!1;;){if(!l){l=i.return;e:for(;;){if(null===l)throw a(Error(160));switch(r=l.stateNode,l.tag){case 5:o=!1;break e;case 3:case 4:r=r.containerInfo,o=!0;break e}l=l.return}l=!0}if(5===i.tag||6===i.tag){e:for(var u=e,s=i,c=n,f=s;;)if(tl(u,f,c),null!==f.child&&4!==f.tag)f.child.return=f,f=f.child;else{if(f===s)break;for(;null===f.sibling;){if(null===f.return||f.return===s)break e;f=f.return}f.sibling.return=f.return,f=f.sibling}o?(u=r,s=i.stateNode,8===u.nodeType?u.parentNode.removeChild(s):u.removeChild(s)):r.removeChild(i.stateNode)}else if(4===i.tag){if(null!==i.child){r=i.stateNode.containerInfo,o=!0,i.child.return=i,i=i.child;continue}}else if(tl(e,i,n),null!==i.child){i.child.return=i,i=i.child;continue}if(i===t)break;for(;null===i.sibling;){if(null===i.return||i.return===t)return;4===(i=i.return).tag&&(l=!1)}i.sibling.return=i.return,i=i.sibling}}function al(e,t){switch(t.tag){case 0:case 11:case 14:case 15:el(4,8,t);break;case 1:break;case 5:var n=t.stateNode;if(null!=n){var r=t.memoizedProps,o=null!==e?e.memoizedProps:r;e=t.type;var i=t.updateQueue;if(t.updateQueue=null,null!==i){for(n[nr]=r,\"input\"===e&&\"radio\"===r.type&&null!=r.name&&xe(n,r),Rn(e,o),t=Rn(e,r),o=0;o<i.length;o+=2){var l=i[o],u=i[o+1];\"style\"===l?Pn(n,u):\"dangerouslySetInnerHTML\"===l?Ve(n,u):\"children\"===l?He(n,u):Ee(n,l,u,t)}switch(e){case\"input\":Ie(n,r);break;case\"textarea\":De(n,r);break;case\"select\":t=n._wrapperState.wasMultiple,n._wrapperState.wasMultiple=!!r.multiple,null!=(e=r.value)?Ne(n,!!r.multiple,e,!1):t!==!!r.multiple&&(null!=r.defaultValue?Ne(n,!!r.multiple,r.defaultValue,!0):Ne(n,!!r.multiple,r.multiple?[]:\"\",!1))}}}break;case 6:if(null===t.stateNode)throw a(Error(162));t.stateNode.nodeValue=t.memoizedProps;break;case 3:(t=t.stateNode).hydrate&&(t.hydrate=!1,mt(t.containerInfo));break;case 12:break;case 13:if(n=t,null===t.memoizedState?r=!1:(r=!0,n=t.child,Ml=jo()),null!==n)e:for(e=n;;){if(5===e.tag)i=e.stateNode,r?\"function\"===typeof(i=i.style).setProperty?i.setProperty(\"display\",\"none\",\"important\"):i.display=\"none\":(i=e.stateNode,o=void 0!==(o=e.memoizedProps.style)&&null!==o&&o.hasOwnProperty(\"display\")?o.display:null,i.style.display=kn(\"display\",o));else if(6===e.tag)e.stateNode.nodeValue=r?\"\":e.memoizedProps;else{if(13===e.tag&&null!==e.memoizedState&&null===e.memoizedState.dehydrated){(i=e.child.sibling).return=e,e=i;continue}if(null!==e.child){e.child.return=e,e=e.child;continue}}if(e===n)break e;for(;null===e.sibling;){if(null===e.return||e.return===n)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}ll(t);break;case 19:ll(t);break;case 17:case 20:case 21:break;default:throw a(Error(163))}}function ll(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new Qa),t.forEach(function(t){var r=function(e,t){var n=e.stateNode;null!==n&&n.delete(t),1===(t=1)&&(t=Ql(t=Yl(),e,null)),null!==(e=eu(e,t))&&nu(e)}.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))})}}var ul=\"function\"===typeof WeakMap?WeakMap:Map;function sl(e,t,n){(n=ii(n,null)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Ul||(Ul=!0,Bl=r),$a(e,t)},n}function cl(e,t,n){(n=ii(n,null)).tag=3;var r=e.type.getDerivedStateFromError;if(\"function\"===typeof r){var o=t.value;n.payload=function(){return $a(e,t),r(o)}}var i=e.stateNode;return null!==i&&\"function\"===typeof i.componentDidCatch&&(n.callback=function(){\"function\"!==typeof r&&(null===Vl?Vl=new Set([this]):Vl.add(this),$a(e,t));var n=t.stack;this.componentDidCatch(t.value,{componentStack:null!==n?n:\"\"})}),n}var fl=Math.ceil,dl=N.ReactCurrentDispatcher,pl=N.ReactCurrentOwner,hl=0,ml=8,yl=16,vl=32,gl=0,bl=1,_l=2,El=3,Tl=4,Cl=5,Ol=6,wl=hl,Sl=null,xl=null,Il=0,Al=gl,kl=null,Pl=1073741823,Nl=1073741823,Ll=null,Rl=0,Dl=!1,Ml=0,jl=500,Fl=null,Ul=!1,Bl=null,Vl=null,Hl=!1,zl=null,Kl=90,Wl=null,Gl=0,ql=null,Xl=0;function Yl(){return(wl&(yl|vl))!==hl?1073741821-(jo()/10|0):0!==Xl?Xl:Xl=1073741821-(jo()/10|0)}function Ql(e,t,n){if(0===(2&(t=t.mode)))return 1073741823;var r=Fo();if(0===(4&t))return 99===r?1073741823:1073741822;if((wl&yl)!==hl)return Il;if(null!==n)e=1073741821-25*(1+((1073741821-e+(0|n.timeoutMs||5e3)/10)/25|0));else switch(r){case 99:e=1073741823;break;case 98:e=1073741821-10*(1+((1073741821-e+15)/10|0));break;case 97:case 96:e=1073741821-25*(1+((1073741821-e+500)/25|0));break;case 95:e=2;break;default:throw a(Error(326))}return null!==Sl&&e===Il&&--e,e}var $l,Zl=0;function Jl(e,t){if(50<Gl)throw Gl=0,ql=null,a(Error(185));if(null!==(e=eu(e,t))){var n=Fo();1073741823===t?(wl&ml)!==hl&&(wl&(yl|vl))===hl?ru(e):(nu(e),wl===hl&&zo()):nu(e),(4&wl)===hl||98!==n&&99!==n||(null===Wl?Wl=new Map([[e,t]]):(void 0===(n=Wl.get(e))||n>t)&&Wl.set(e,t))}}function eu(e,t){e.expirationTime<t&&(e.expirationTime=t);var n=e.alternate;null!==n&&n.expirationTime<t&&(n.expirationTime=t);var r=e.return,o=null;if(null===r&&3===e.tag)o=e.stateNode;else for(;null!==r;){if(n=r.alternate,r.childExpirationTime<t&&(r.childExpirationTime=t),null!==n&&n.childExpirationTime<t&&(n.childExpirationTime=t),null===r.return&&3===r.tag){o=r.stateNode;break}r=r.return}return null!==o&&(Sl===o&&(pu(t),Al===Tl&&Fu(o,Il)),Uu(o,t)),o}function tu(e){var t=e.lastExpiredTime;return 0!==t?t:ju(e,t=e.firstPendingTime)?(t=e.lastPingedTime)>(e=e.nextKnownPendingLevel)?t:e:t}function nu(e){if(0!==e.lastExpiredTime)e.callbackExpirationTime=1073741823,e.callbackPriority=99,e.callbackNode=Ho(ru.bind(null,e));else{var t=tu(e),n=e.callbackNode;if(0===t)null!==n&&(e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90);else{var r=Yl();if(1073741823===t?r=99:1===t||2===t?r=95:r=0>=(r=10*(1073741821-t)-10*(1073741821-r))?99:250>=r?98:5250>=r?97:95,null!==n){var o=e.callbackPriority;if(e.callbackExpirationTime===t&&o>=r)return;n!==Po&&Eo(n)}e.callbackExpirationTime=t,e.callbackPriority=r,t=1073741823===t?Ho(ru.bind(null,e)):Vo(r,function e(t,n){Xl=0;if(n)return n=Yl(),Bu(t,n),nu(t),null;var r=tu(t);if(0!==r){if(n=t.callbackNode,(wl&(yl|vl))!==hl)throw a(Error(327));if(Eu(),t===Sl&&r===Il||su(t,r),null!==xl){var o=wl;wl|=yl;for(var i=fu();;)try{mu();break}catch(s){cu(t,s)}if(Qo(),wl=o,dl.current=i,Al===bl)throw n=kl,su(t,r),Fu(t,r),nu(t),n;if(null===xl)switch(i=t.finishedWork=t.current.alternate,t.finishedExpirationTime=r,iu(t,r),o=Al,Sl=null,o){case gl:case bl:throw a(Error(345));case _l:if(2!==r){Bu(t,2);break}bu(t);break;case El:if(Fu(t,r),o=t.lastSuspendedTime,r===o&&(t.nextKnownPendingLevel=gu(i)),1073741823===Pl&&10<(i=Ml+jl-jo())){if(Dl){var l=t.lastPingedTime;if(0===l||l>=r){t.lastPingedTime=r,su(t,r);break}}if(0!==(l=tu(t))&&l!==r)break;if(0!==o&&o!==r){t.lastPingedTime=o;break}t.timeoutHandle=Qn(bu.bind(null,t),i);break}bu(t);break;case Tl:if(Fu(t,r),o=t.lastSuspendedTime,r===o&&(t.nextKnownPendingLevel=gu(i)),Dl&&(0===(i=t.lastPingedTime)||i>=r)){t.lastPingedTime=r,su(t,r);break}if(0!==(i=tu(t))&&i!==r)break;if(0!==o&&o!==r){t.lastPingedTime=o;break}if(1073741823!==Nl?o=10*(1073741821-Nl)-jo():1073741823===Pl?o=0:(o=10*(1073741821-Pl)-5e3,i=jo(),r=10*(1073741821-r)-i,0>(o=i-o)&&(o=0),o=(120>o?120:480>o?480:1080>o?1080:1920>o?1920:3e3>o?3e3:4320>o?4320:1960*fl(o/1960))-o,r<o&&(o=r)),10<o){t.timeoutHandle=Qn(bu.bind(null,t),o);break}bu(t);break;case Cl:if(1073741823!==Pl&&null!==Ll){l=Pl;var u=Ll;if(0>=(o=0|u.busyMinDurationMs)?o=0:(i=0|u.busyDelayMs,l=jo()-(10*(1073741821-l)-(0|u.timeoutMs||5e3)),o=l<=i?0:i+o-l),10<o){Fu(t,r),t.timeoutHandle=Qn(bu.bind(null,t),o);break}}bu(t);break;case Ol:Fu(t,r);break;default:throw a(Error(329))}if(nu(t),t.callbackNode===n)return e.bind(null,t)}}return null}.bind(null,e),{timeout:10*(1073741821-t)-jo()}),e.callbackNode=t}}}function ru(e){var t=e.lastExpiredTime;if(t=0!==t?t:1073741823,e.finishedExpirationTime===t)bu(e);else{if((wl&(yl|vl))!==hl)throw a(Error(327));if(Eu(),e===Sl&&t===Il||su(e,t),null!==xl){var n=wl;wl|=yl;for(var r=fu();;)try{hu();break}catch(o){cu(e,o)}if(Qo(),wl=n,dl.current=r,Al===bl)throw n=kl,su(e,t),Fu(e,t),nu(e),n;if(null!==xl)throw a(Error(261));e.finishedWork=e.current.alternate,e.finishedExpirationTime=t,iu(e,t),Al===Ol?Fu(e,t):(Sl=null,bu(e)),nu(e)}}return null}function ou(){(wl&(1|yl|vl))===hl&&(function(){if(null!==Wl){var e=Wl;Wl=null,e.forEach(function(e,t){Bu(t,e),nu(t)}),zo()}}(),Eu())}function iu(e,t){var n=e.firstBatch;null!==n&&n._defer&&n._expirationTime>=t&&(Vo(97,function(){return n._onComplete(),null}),Al=Ol)}function au(e,t){var n=wl;wl|=1;try{return e(t)}finally{(wl=n)===hl&&zo()}}function lu(e,t,n,r){var o=wl;wl|=4;try{return Bo(98,e.bind(null,t,n,r))}finally{(wl=o)===hl&&zo()}}function uu(e,t){var n=wl;wl&=-2,wl|=ml;try{return e(t)}finally{(wl=n)===hl&&zo()}}function su(e,t){e.finishedWork=null,e.finishedExpirationTime=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,$n(n)),null!==xl)for(n=xl.return;null!==n;){var r=n;switch(r.tag){case 1:var o=r.type.childContextTypes;null!==o&&void 0!==o&&po();break;case 3:Ri(),ho();break;case 5:Mi(r);break;case 4:Ri();break;case 13:case 19:oo(ji);break;case 10:Zo(r)}n=n.return}Sl=e,xl=Pu(e.current,null),Il=t,Al=gl,kl=null,Nl=Pl=1073741823,Ll=null,Rl=0,Dl=!1}function cu(e,t){for(;;){try{if(Qo(),ra(),null===xl||null===xl.return)return Al=bl,kl=t,null;e:{var n=e,r=xl.return,o=xl,i=t;if(t=Il,o.effectTag|=2048,o.firstEffect=o.lastEffect=null,null!==i&&\"object\"===typeof i&&\"function\"===typeof i.then){var a=i,l=0!==(1&ji.current),u=r;do{var s;if(s=13===u.tag){var c=u.memoizedState;if(null!==c)s=null!==c.dehydrated;else{var f=u.memoizedProps;s=void 0!==f.fallback&&(!0!==f.unstable_avoidThisFallback||!l)}}if(s){var d=u.updateQueue;if(null===d){var p=new Set;p.add(a),u.updateQueue=p}else d.add(a);if(0===(2&u.mode)){if(u.effectTag|=64,o.effectTag&=-2981,1===o.tag)if(null===o.alternate)o.tag=17;else{var h=ii(1073741823,null);h.tag=2,li(o,h)}o.expirationTime=1073741823;break e}i=void 0,o=t;var m=n.pingCache;if(null===m?(m=n.pingCache=new ul,i=new Set,m.set(a,i)):void 0===(i=m.get(a))&&(i=new Set,m.set(a,i)),!i.has(o)){i.add(o);var y=wu.bind(null,n,a,o);a.then(y,y)}u.effectTag|=4096,u.expirationTime=t;break e}u=u.return}while(null!==u);i=Error((Q(o.type)||\"A React component\")+\" suspended while rendering, but no fallback UI was specified.\\n\\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.\"+$(o))}Al!==Cl&&(Al=_l),i=Ya(i,o),u=r;do{switch(u.tag){case 3:a=i,u.effectTag|=4096,u.expirationTime=t,ui(u,sl(u,a,t));break e;case 1:a=i;var v=u.type,g=u.stateNode;if((64&u.effectTag)===yt&&(\"function\"===typeof v.getDerivedStateFromError||null!==g&&\"function\"===typeof g.componentDidCatch&&(null===Vl||!Vl.has(g)))){u.effectTag|=4096,u.expirationTime=t,ui(u,cl(u,a,t));break e}}u=u.return}while(null!==u)}xl=vu(xl)}catch(b){t=b;continue}break}}function fu(){var e=dl.current;return dl.current=ha,null===e?ha:e}function du(e,t){e<Pl&&2<e&&(Pl=e),null!==t&&e<Nl&&2<e&&(Nl=e,Ll=t)}function pu(e){e>Rl&&(Rl=e)}function hu(){for(;null!==xl;)xl=yu(xl)}function mu(){for(;null!==xl&&!To();)xl=yu(xl)}function yu(e){var t=$l(e.alternate,e,Il);return e.memoizedProps=e.pendingProps,null===t&&(t=vu(e)),pl.current=null,t}function vu(e){xl=e;do{var t=xl.alternate;if(e=xl.return,(2048&xl.effectTag)===yt){e:{var n=t,r=Il,i=(t=xl).pendingProps;switch(t.tag){case 2:case 16:break;case 15:case 0:break;case 1:fo(t.type)&&po();break;case 3:Ri(),ho(),(r=t.stateNode).pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),(null===n||null===n.child)&&Oa(t)&&Ga(t),Fa(t);break;case 5:Mi(t),r=Ni(Pi.current);var l=t.type;if(null!==n&&null!=t.stateNode)Ua(n,t,l,i,r),n.ref!==t.ref&&(t.effectTag|=128);else if(i){var u=Ni(Ai.current);if(Oa(t)){l=void 0,n=(i=t).stateNode;var s=i.type,c=i.memoizedProps;switch(n[tr]=i,n[nr]=c,s){case\"iframe\":case\"object\":case\"embed\":bn(\"load\",n);break;case\"video\":case\"audio\":for(var f=0;f<Ze.length;f++)bn(Ze[f],n);break;case\"source\":bn(\"error\",n);break;case\"img\":case\"image\":case\"link\":bn(\"error\",n),bn(\"load\",n);break;case\"form\":bn(\"reset\",n),bn(\"submit\",n);break;case\"details\":bn(\"toggle\",n);break;case\"input\":Se(n,c),bn(\"invalid\",n),Dn(r,\"onChange\");break;case\"select\":n._wrapperState={wasMultiple:!!c.multiple},bn(\"invalid\",n),Dn(r,\"onChange\");break;case\"textarea\":Re(n,c),bn(\"invalid\",n),Dn(r,\"onChange\")}for(l in Ln(s,c),f=null,c)c.hasOwnProperty(l)&&(u=c[l],\"children\"===l?\"string\"===typeof u?n.textContent!==u&&(f=[\"children\",u]):\"number\"===typeof u&&n.textContent!==\"\"+u&&(f=[\"children\",\"\"+u]):p.hasOwnProperty(l)&&null!=u&&Dn(r,l));switch(s){case\"input\":Ce(n),Ae(n,c,!0);break;case\"textarea\":Ce(n),Me(n);break;case\"select\":case\"option\":break;default:\"function\"===typeof c.onClick&&(n.onclick=Mn)}r=f,i.updateQueue=r,null!==r&&Ga(t)}else{c=l,n=i,s=t,f=9===r.nodeType?r:r.ownerDocument,u===je.html&&(u=Fe(c)),u===je.html?\"script\"===c?((c=f.createElement(\"div\")).innerHTML=\"<script><\\/script>\",f=c.removeChild(c.firstChild)):\"string\"===typeof n.is?f=f.createElement(c,{is:n.is}):(f=f.createElement(c),\"select\"===c&&(c=f,n.multiple?c.multiple=!0:n.size&&(c.size=n.size))):f=f.createElementNS(u,c),(c=f)[tr]=s,c[nr]=n,ja(n=c,t,!1,!1),t.stateNode=n,u=r;var d=Rn(l,i);switch(l){case\"iframe\":case\"object\":case\"embed\":bn(\"load\",n),r=i;break;case\"video\":case\"audio\":for(r=0;r<Ze.length;r++)bn(Ze[r],n);r=i;break;case\"source\":bn(\"error\",n),r=i;break;case\"img\":case\"image\":case\"link\":bn(\"error\",n),bn(\"load\",n),r=i;break;case\"form\":bn(\"reset\",n),bn(\"submit\",n),r=i;break;case\"details\":bn(\"toggle\",n),r=i;break;case\"input\":Se(n,i),r=we(n,i),bn(\"invalid\",n),Dn(u,\"onChange\");break;case\"option\":r=Pe(n,i);break;case\"select\":n._wrapperState={wasMultiple:!!i.multiple},r=o({},i,{value:void 0}),bn(\"invalid\",n),Dn(u,\"onChange\");break;case\"textarea\":Re(n,i),r=Le(n,i),bn(\"invalid\",n),Dn(u,\"onChange\");break;default:r=i}Ln(l,r),s=void 0,c=l,f=n;var h=r;for(s in h)if(h.hasOwnProperty(s)){var m=h[s];\"style\"===s?Pn(f,m):\"dangerouslySetInnerHTML\"===s?null!=(m=m?m.__html:void 0)&&Ve(f,m):\"children\"===s?\"string\"===typeof m?(\"textarea\"!==c||\"\"!==m)&&He(f,m):\"number\"===typeof m&&He(f,\"\"+m):\"suppressContentEditableWarning\"!==s&&\"suppressHydrationWarning\"!==s&&\"autoFocus\"!==s&&(p.hasOwnProperty(s)?null!=m&&Dn(u,s):null!=m&&Ee(f,s,m,d))}switch(l){case\"input\":Ce(n),Ae(n,i,!1);break;case\"textarea\":Ce(n),Me(n);break;case\"option\":null!=i.value&&n.setAttribute(\"value\",\"\"+_e(i.value));break;case\"select\":r=n,n=i,r.multiple=!!n.multiple,null!=(s=n.value)?Ne(r,!!n.multiple,s,!1):null!=n.defaultValue&&Ne(r,!!n.multiple,n.defaultValue,!0);break;default:\"function\"===typeof r.onClick&&(n.onclick=Mn)}Xn(l,i)&&Ga(t)}null!==t.ref&&(t.effectTag|=128)}else if(null===t.stateNode)throw a(Error(166));break;case 6:if(n&&null!=t.stateNode)Ba(n,t,n.memoizedProps,i);else{if(\"string\"!==typeof i&&null===t.stateNode)throw a(Error(166));l=Ni(Pi.current),Ni(Ai.current),Oa(t)?(r=t.stateNode,i=t.memoizedProps,r[tr]=t,r.nodeValue!==i&&Ga(t)):(r=t,(i=(9===l.nodeType?l:l.ownerDocument).createTextNode(i))[tr]=t,r.stateNode=i)}break;case 11:break;case 13:if(oo(ji),i=t.memoizedState,(64&t.effectTag)!==yt){t.expirationTime=r;break e}r=null!==i,i=!1,null===n?Oa(t):(i=null!==(l=n.memoizedState),r||null===l||null!==(l=n.child.sibling)&&(null!==(s=t.firstEffect)?(t.firstEffect=l,l.nextEffect=s):(t.firstEffect=t.lastEffect=l,l.nextEffect=null),l.effectTag=8)),r&&!i&&0!==(2&t.mode)&&(null===n&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!==(1&ji.current)?Al===gl&&(Al=El):(Al!==gl&&Al!==El||(Al=Tl),0!==Rl&&null!==Sl&&(Fu(Sl,Il),Uu(Sl,Rl)))),(r||i)&&(t.effectTag|=4);break;case 7:case 8:case 12:break;case 4:Ri(),Fa(t);break;case 10:Zo(t);break;case 9:case 14:break;case 17:fo(t.type)&&po();break;case 19:if(oo(ji),null===(i=t.memoizedState))break;if(l=(64&t.effectTag)!==yt,null===(s=i.rendering)){if(l)qa(i,!1);else if(Al!==gl||null!==n&&(64&n.effectTag)!==yt)for(n=t.child;null!==n;){if(null!==(s=Fi(n))){for(t.effectTag|=64,qa(i,!1),null!==(i=s.updateQueue)&&(t.updateQueue=i,t.effectTag|=4),t.firstEffect=t.lastEffect=null,i=t.child;null!==i;)n=r,(l=i).effectTag&=vt,l.nextEffect=null,l.firstEffect=null,l.lastEffect=null,null===(s=l.alternate)?(l.childExpirationTime=0,l.expirationTime=n,l.child=null,l.memoizedProps=null,l.memoizedState=null,l.updateQueue=null,l.dependencies=null):(l.childExpirationTime=s.childExpirationTime,l.expirationTime=s.expirationTime,l.child=s.child,l.memoizedProps=s.memoizedProps,l.memoizedState=s.memoizedState,l.updateQueue=s.updateQueue,n=s.dependencies,l.dependencies=null===n?null:{expirationTime:n.expirationTime,firstContext:n.firstContext,responders:n.responders}),i=i.sibling;io(ji,1&ji.current|2),t=t.child;break e}n=n.sibling}}else{if(!l)if(null!==(n=Fi(s))){if(t.effectTag|=64,l=!0,qa(i,!0),null===i.tail&&\"hidden\"===i.tailMode){null!==(r=n.updateQueue)&&(t.updateQueue=r,t.effectTag|=4),null!==(t=t.lastEffect=i.lastEffect)&&(t.nextEffect=null);break}}else jo()>i.tailExpiration&&1<r&&(t.effectTag|=64,l=!0,qa(i,!1),t.expirationTime=t.childExpirationTime=r-1);i.isBackwards?(s.sibling=t.child,t.child=s):(null!==(r=i.last)?r.sibling=s:t.child=s,i.last=s)}if(null!==i.tail){0===i.tailExpiration&&(i.tailExpiration=jo()+500),r=i.tail,i.rendering=r,i.tail=r.sibling,i.lastEffect=t.lastEffect,r.sibling=null,i=ji.current,io(ji,i=l?1&i|2:1&i),t=r;break e}break;case 20:case 21:break;default:throw a(Error(156),t.tag)}t=null}if(r=xl,1===Il||1!==r.childExpirationTime){for(i=0,l=r.child;null!==l;)(n=l.expirationTime)>i&&(i=n),(s=l.childExpirationTime)>i&&(i=s),l=l.sibling;r.childExpirationTime=i}if(null!==t)return t;null!==e&&(2048&e.effectTag)===yt&&(null===e.firstEffect&&(e.firstEffect=xl.firstEffect),null!==xl.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=xl.firstEffect),e.lastEffect=xl.lastEffect),1<xl.effectTag&&(null!==e.lastEffect?e.lastEffect.nextEffect=xl:e.firstEffect=xl,e.lastEffect=xl))}else{if(null!==(t=Xa(xl)))return t.effectTag&=2047,t;null!==e&&(e.firstEffect=e.lastEffect=null,e.effectTag|=2048)}if(null!==(t=xl.sibling))return t;xl=e}while(null!==xl);return Al===gl&&(Al=Cl),null}function gu(e){var t=e.expirationTime;return t>(e=e.childExpirationTime)?t:e}function bu(e){var t=Fo();return Bo(99,function(e,t){if(Eu(),(wl&(yl|vl))!==hl)throw a(Error(327));var n=e.finishedWork,r=e.finishedExpirationTime;if(null===n)return null;if(e.finishedWork=null,e.finishedExpirationTime=0,n===e.current)throw a(Error(177));e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90,e.nextKnownPendingLevel=0;var o=gu(n);if(e.firstPendingTime=o,r<=e.lastSuspendedTime?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:r<=e.firstSuspendedTime&&(e.firstSuspendedTime=r-1),r<=e.lastPingedTime&&(e.lastPingedTime=0),r<=e.lastExpiredTime&&(e.lastExpiredTime=0),e===Sl&&(xl=Sl=null,Il=0),1<n.effectTag?null!==n.lastEffect?(n.lastEffect.nextEffect=n,o=n.firstEffect):o=n:o=n.firstEffect,null!==o){var i=wl;wl|=vl,pl.current=null,Gn=gn;var l=Bn();if(Vn(l)){if(\"selectionStart\"in l)var u={start:l.selectionStart,end:l.selectionEnd};else e:{var s=(u=(u=l.ownerDocument)&&u.defaultView||window).getSelection&&u.getSelection();if(s&&0!==s.rangeCount){u=s.anchorNode;var c=s.anchorOffset,f=s.focusNode;s=s.focusOffset;try{u.nodeType,f.nodeType}catch(M){u=null;break e}var d=0,p=-1,h=-1,m=0,y=0,v=l,g=null;t:for(;;){for(var b;v!==u||0!==c&&3!==v.nodeType||(p=d+c),v!==f||0!==s&&3!==v.nodeType||(h=d+s),3===v.nodeType&&(d+=v.nodeValue.length),null!==(b=v.firstChild);)g=v,v=b;for(;;){if(v===l)break t;if(g===u&&++m===c&&(p=d),g===f&&++y===s&&(h=d),null!==(b=v.nextSibling))break;g=(v=g).parentNode}v=b}u=-1===p||-1===h?null:{start:p,end:h}}else u=null}u=u||{start:0,end:0}}else u=null;qn={focusedElem:l,selectionRange:u},gn=!1,Fl=o;do{try{_u()}catch(M){if(null===Fl)throw a(Error(330));Ou(Fl,M),Fl=Fl.nextEffect}}while(null!==Fl);Fl=o;do{try{for(l=e,u=t;null!==Fl;){var _=Fl.effectTag;if(16&_&&He(Fl.stateNode,\"\"),128&_){var E=Fl.alternate;if(null!==E){var T=E.ref;null!==T&&(\"function\"===typeof T?T(null):T.current=null)}}switch(_&(12|vt|gt)){case vt:ol(Fl),Fl.effectTag&=~vt;break;case 6:ol(Fl),Fl.effectTag&=~vt,al(Fl.alternate,Fl);break;case gt:Fl.effectTag&=~gt;break;case 1028:Fl.effectTag&=~gt,al(Fl.alternate,Fl);break;case 4:al(Fl.alternate,Fl);break;case 8:il(l,c=Fl,u),nl(c)}Fl=Fl.nextEffect}}catch(M){if(null===Fl)throw a(Error(330));Ou(Fl,M),Fl=Fl.nextEffect}}while(null!==Fl);if(T=qn,E=Bn(),_=T.focusedElem,u=T.selectionRange,E!==_&&_&&_.ownerDocument&&function e(t,n){return!(!t||!n)&&(t===n||(!t||3!==t.nodeType)&&(n&&3===n.nodeType?e(t,n.parentNode):\"contains\"in t?t.contains(n):!!t.compareDocumentPosition&&!!(16&t.compareDocumentPosition(n))))}(_.ownerDocument.documentElement,_)){null!==u&&Vn(_)&&(E=u.start,void 0===(T=u.end)&&(T=E),\"selectionStart\"in _?(_.selectionStart=E,_.selectionEnd=Math.min(T,_.value.length)):(T=(E=_.ownerDocument||document)&&E.defaultView||window).getSelection&&(T=T.getSelection(),c=_.textContent.length,l=Math.min(u.start,c),u=void 0===u.end?l:Math.min(u.end,c),!T.extend&&l>u&&(c=u,u=l,l=c),c=Un(_,l),f=Un(_,u),c&&f&&(1!==T.rangeCount||T.anchorNode!==c.node||T.anchorOffset!==c.offset||T.focusNode!==f.node||T.focusOffset!==f.offset)&&((E=E.createRange()).setStart(c.node,c.offset),T.removeAllRanges(),l>u?(T.addRange(E),T.extend(f.node,f.offset)):(E.setEnd(f.node,f.offset),T.addRange(E))))),E=[];for(T=_;T=T.parentNode;)1===T.nodeType&&E.push({element:T,left:T.scrollLeft,top:T.scrollTop});for(\"function\"===typeof _.focus&&_.focus(),_=0;_<E.length;_++)(T=E[_]).element.scrollLeft=T.left,T.element.scrollTop=T.top}qn=null,gn=!!Gn,Gn=null,e.current=n,Fl=o;do{try{for(_=r;null!==Fl;){var C=Fl.effectTag;if(36&C){var O=Fl.alternate;switch(T=_,(E=Fl).tag){case 0:case 11:case 15:el(16,32,E);break;case 1:var w=E.stateNode;if(4&E.effectTag)if(null===O)w.componentDidMount();else{var S=E.elementType===E.type?O.memoizedProps:Wo(E.type,O.memoizedProps);w.componentDidUpdate(S,O.memoizedState,w.__reactInternalSnapshotBeforeUpdate)}var x=E.updateQueue;null!==x&&di(0,x,w);break;case 3:var I=E.updateQueue;if(null!==I){if(l=null,null!==E.child)switch(E.child.tag){case 5:l=E.child.stateNode;break;case 1:l=E.child.stateNode}di(0,I,l)}break;case 5:var A=E.stateNode;null===O&&4&E.effectTag&&(T=A,Xn(E.type,E.memoizedProps)&&T.focus());break;case 6:case 4:case 12:break;case 13:if(null===E.memoizedState){var k=E.alternate;if(null!==k){var P=k.memoizedState;if(null!==P){var N=P.dehydrated;null!==N&&mt(N)}}}break;case 19:case 17:case 20:case 21:break;default:throw a(Error(163))}}if(128&C){var L=(E=Fl).ref;if(null!==L){var R=E.stateNode;switch(E.tag){case 5:var D=R;break;default:D=R}\"function\"===typeof L?L(D):L.current=D}}Fl=Fl.nextEffect}}catch(M){if(null===Fl)throw a(Error(330));Ou(Fl,M),Fl=Fl.nextEffect}}while(null!==Fl);Fl=null,No(),wl=i}else e.current=n;if(Hl)Hl=!1,zl=e,Kl=t;else for(Fl=o;null!==Fl;)t=Fl.nextEffect,Fl.nextEffect=null,Fl=t;if(0===(t=e.firstPendingTime)&&(Vl=null),1073741823===t?e===ql?Gl++:(Gl=0,ql=e):Gl=0,\"function\"===typeof Su&&Su(n.stateNode,r),nu(e),Ul)throw Ul=!1,e=Bl,Bl=null,e;return(wl&ml)!==hl?null:(zo(),null)}.bind(null,e,t)),null}function _u(){for(;null!==Fl;){var e=Fl.effectTag;(256&e)!==yt&&Ja(Fl.alternate,Fl),(512&e)===yt||Hl||(Hl=!0,Vo(97,function(){return Eu(),null})),Fl=Fl.nextEffect}}function Eu(){if(90!==Kl){var e=97<Kl?97:Kl;return Kl=90,Bo(e,Tu)}}function Tu(){if(null===zl)return!1;var e=zl;if(zl=null,(wl&(yl|vl))!==hl)throw a(Error(331));var t=wl;for(wl|=vl,e=e.current.firstEffect;null!==e;){try{var n=e;if((512&n.effectTag)!==yt)switch(n.tag){case 0:case 11:case 15:el(128,0,n),el(0,64,n)}}catch(r){if(null===e)throw a(Error(330));Ou(e,r)}n=e.nextEffect,e.nextEffect=null,e=n}return wl=t,zo(),!0}function Cu(e,t,n){li(e,t=sl(e,t=Ya(n,t),1073741823)),null!==(e=eu(e,1073741823))&&nu(e)}function Ou(e,t){if(3===e.tag)Cu(e,e,t);else for(var n=e.return;null!==n;){if(3===n.tag){Cu(n,e,t);break}if(1===n.tag){var r=n.stateNode;if(\"function\"===typeof n.type.getDerivedStateFromError||\"function\"===typeof r.componentDidCatch&&(null===Vl||!Vl.has(r))){li(n,e=cl(n,e=Ya(t,e),1073741823)),null!==(n=eu(n,1073741823))&&nu(n);break}}n=n.return}}function wu(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),Sl===e&&Il===n?Al===Tl||Al===El&&1073741823===Pl&&jo()-Ml<jl?su(e,Il):Dl=!0:ju(e,n)&&(0!==(t=e.lastPingedTime)&&t<n||(e.lastPingedTime=n,e.finishedExpirationTime===n&&(e.finishedExpirationTime=0,e.finishedWork=null),nu(e)))}$l=function(e,t,n){var r=t.expirationTime;if(null!==e){var o=t.pendingProps;if(e.memoizedProps!==o||uo.current)xa=!0;else{if(r<n){switch(xa=!1,t.tag){case 3:Ma(t),wa();break;case 5:if(Di(t),4&t.mode&&1!==n&&o.hidden)return t.expirationTime=t.childExpirationTime=1,null;break;case 1:fo(t.type)&&vo(t);break;case 4:Li(t,t.stateNode.containerInfo);break;case 10:$o(t,t.memoizedProps.value);break;case 13:if(null!==t.memoizedState)return 0!==(r=t.child.childExpirationTime)&&r>=n?Ha(e,t,n):(io(ji,1&ji.current),null!==(t=Wa(e,t,n))?t.sibling:null);io(ji,1&ji.current);break;case 19:if(r=t.childExpirationTime>=n,(64&e.effectTag)!==yt){if(r)return Ka(e,t,n);t.effectTag|=64}if(null!==(o=t.memoizedState)&&(o.rendering=null,o.tail=null),io(ji,ji.current),!r)return null}return Wa(e,t,n)}xa=!1}}else xa=!1;switch(t.expirationTime=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=vt),e=t.pendingProps,o=co(t,lo.current),ei(t,n),o=na(null,t,r,e,o,n),t.effectTag|=1,\"object\"===typeof o&&null!==o&&\"function\"===typeof o.render&&void 0===o.$$typeof){if(t.tag=1,ra(),fo(r)){var i=!0;vo(t)}else i=!1;t.memoizedState=null!==o.state&&void 0!==o.state?o.state:null;var l=r.getDerivedStateFromProps;\"function\"===typeof l&&yi(t,r,l,e),o.updater=vi,t.stateNode=o,o._reactInternalFiber=t,Ei(t,r,e,n),t=Da(null,t,r,!0,i,n)}else t.tag=0,Ia(null,t,o,n),t=t.child;return t;case 16:if(o=t.elementType,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=vt),e=t.pendingProps,function(e){if(-1===e._status){e._status=0;var t=e._ctor;t=t(),e._result=t,t.then(function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)},function(t){0===e._status&&(e._status=2,e._result=t)})}}(o),1!==o._status)throw o._result;switch(o=o._result,t.type=o,i=t.tag=function(e){if(\"function\"===typeof e)return ku(e)?1:0;if(void 0!==e&&null!==e){if((e=e.$$typeof)===z)return 11;if(e===G)return 14}return 2}(o),e=Wo(o,e),i){case 0:t=La(null,t,o,e,n);break;case 1:t=Ra(null,t,o,e,n);break;case 11:t=Aa(null,t,o,e,n);break;case 14:t=ka(null,t,o,Wo(o.type,e),r,n);break;default:throw a(Error(306),o,\"\")}return t;case 0:return r=t.type,o=t.pendingProps,La(e,t,r,o=t.elementType===r?o:Wo(r,o),n);case 1:return r=t.type,o=t.pendingProps,Ra(e,t,r,o=t.elementType===r?o:Wo(r,o),n);case 3:if(Ma(t),null===(r=t.updateQueue))throw a(Error(282));if(o=null!==(o=t.memoizedState)?o.element:null,fi(t,r,t.pendingProps,null,n),(r=t.memoizedState.element)===o)wa(),t=Wa(e,t,n);else{if((o=t.stateNode.hydrate)&&(ga=Zn(t.stateNode.containerInfo.firstChild),va=t,o=ba=!0),o)for(n=xi(t,null,r,n),t.child=n;n;)n.effectTag=n.effectTag&~vt|gt,n=n.sibling;else Ia(e,t,r,n),wa();t=t.child}return t;case 5:return Di(t),null===e&&Ta(t),r=t.type,o=t.pendingProps,i=null!==e?e.memoizedProps:null,l=o.children,Yn(r,o)?l=null:null!==i&&Yn(r,i)&&(t.effectTag|=16),Na(e,t),4&t.mode&&1!==n&&o.hidden?(t.expirationTime=t.childExpirationTime=1,t=null):(Ia(e,t,l,n),t=t.child),t;case 6:return null===e&&Ta(t),null;case 13:return Ha(e,t,n);case 4:return Li(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=Si(t,null,r,n):Ia(e,t,r,n),t.child;case 11:return r=t.type,o=t.pendingProps,Aa(e,t,r,o=t.elementType===r?o:Wo(r,o),n);case 7:return Ia(e,t,t.pendingProps,n),t.child;case 8:case 12:return Ia(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,l=t.memoizedProps,$o(t,i=o.value),null!==l){var u=l.value;if(0===(i=Wr(u,i)?0:0|(\"function\"===typeof r._calculateChangedBits?r._calculateChangedBits(u,i):1073741823))){if(l.children===o.children&&!uo.current){t=Wa(e,t,n);break e}}else for(null!==(u=t.child)&&(u.return=t);null!==u;){var s=u.dependencies;if(null!==s){l=u.child;for(var c=s.firstContext;null!==c;){if(c.context===r&&0!==(c.observedBits&i)){1===u.tag&&((c=ii(n,null)).tag=2,li(u,c)),u.expirationTime<n&&(u.expirationTime=n),null!==(c=u.alternate)&&c.expirationTime<n&&(c.expirationTime=n),Jo(u.return,n),s.expirationTime<n&&(s.expirationTime=n);break}c=c.next}}else l=10===u.tag&&u.type===t.type?null:u.child;if(null!==l)l.return=u;else for(l=u;null!==l;){if(l===t){l=null;break}if(null!==(u=l.sibling)){u.return=l.return,l=u;break}l=l.return}u=l}}Ia(e,t,o.children,n),t=t.child}return t;case 9:return o=t.type,r=(i=t.pendingProps).children,ei(t,n),r=r(o=ti(o,i.unstable_observedBits)),t.effectTag|=1,Ia(e,t,r,n),t.child;case 14:return i=Wo(o=t.type,t.pendingProps),ka(e,t,o,i=Wo(o.type,i),r,n);case 15:return Pa(e,t,t.type,t.pendingProps,r,n);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:Wo(r,o),null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=vt),t.tag=1,fo(r)?(e=!0,vo(t)):e=!1,ei(t,n),bi(t,r,o),Ei(t,r,o,n),Da(null,t,r,!0,e,n);case 19:return Ka(e,t,n)}throw a(Error(156),t.tag)};var Su=null,xu=null;function Iu(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.effectTag=yt,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childExpirationTime=this.expirationTime=0,this.alternate=null}function Au(e,t,n,r){return new Iu(e,t,n,r)}function ku(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Pu(e,t){var n=e.alternate;return null===n?((n=Au(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.effectTag=yt,n.nextEffect=null,n.firstEffect=null,n.lastEffect=null),n.childExpirationTime=e.childExpirationTime,n.expirationTime=e.expirationTime,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{expirationTime:t.expirationTime,firstContext:t.firstContext,responders:t.responders},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Nu(e,t,n,r,o,i){var l=2;if(r=e,\"function\"===typeof e)ku(e)&&(l=1);else if(\"string\"===typeof e)l=5;else e:switch(e){case j:return Lu(n.children,o,i,t);case H:l=8,o|=7;break;case F:l=8,o|=1;break;case U:return(e=Au(12,n,t,8|o)).elementType=U,e.type=U,e.expirationTime=i,e;case K:return(e=Au(13,n,t,o)).type=K,e.elementType=K,e.expirationTime=i,e;case W:return(e=Au(19,n,t,o)).elementType=W,e.expirationTime=i,e;default:if(\"object\"===typeof e&&null!==e)switch(e.$$typeof){case B:l=10;break e;case V:l=9;break e;case z:l=11;break e;case G:l=14;break e;case q:l=16,r=null;break e}throw a(Error(130),null==e?e:typeof e,\"\")}return(t=Au(l,n,t,o)).elementType=e,t.type=r,t.expirationTime=i,t}function Lu(e,t,n,r){return(e=Au(7,e,r,t)).expirationTime=n,e}function Ru(e,t,n){return(e=Au(6,e,null,t)).expirationTime=n,e}function Du(e,t,n){return(t=Au(4,null!==e.children?e.children:[],e.key,t)).expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Mu(e,t,n){this.tag=t,this.current=null,this.containerInfo=e,this.pingCache=this.pendingChildren=null,this.finishedExpirationTime=0,this.finishedWork=null,this.timeoutHandle=-1,this.pendingContext=this.context=null,this.hydrate=n,this.callbackNode=this.firstBatch=null,this.callbackPriority=90,this.lastExpiredTime=this.lastPingedTime=this.nextKnownPendingLevel=this.lastSuspendedTime=this.firstSuspendedTime=this.firstPendingTime=0}function ju(e,t){var n=e.firstSuspendedTime;return e=e.lastSuspendedTime,0!==n&&n>=t&&e<=t}function Fu(e,t){var n=e.firstSuspendedTime,r=e.lastSuspendedTime;n<t&&(e.firstSuspendedTime=t),(r>t||0===n)&&(e.lastSuspendedTime=t),t<=e.lastPingedTime&&(e.lastPingedTime=0),t<=e.lastExpiredTime&&(e.lastExpiredTime=0)}function Uu(e,t){t>e.firstPendingTime&&(e.firstPendingTime=t);var n=e.firstSuspendedTime;0!==n&&(t>=n?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:t>=e.lastSuspendedTime&&(e.lastSuspendedTime=t+1),t>e.nextKnownPendingLevel&&(e.nextKnownPendingLevel=t))}function Bu(e,t){var n=e.lastExpiredTime;(0===n||n>t)&&(e.lastExpiredTime=t)}function Vu(e,t,n,r,o,i){var l=t.current;e:if(n){t:{if(bt(n=n._reactInternalFiber)!==n||1!==n.tag)throw a(Error(170));var u=n;do{switch(u.tag){case 3:u=u.stateNode.context;break t;case 1:if(fo(u.type)){u=u.stateNode.__reactInternalMemoizedMergedChildContext;break t}}u=u.return}while(null!==u);throw a(Error(171))}if(1===n.tag){var s=n.type;if(fo(s)){n=yo(n,s,u);break e}}n=u}else n=ao;return null===t.context?t.context=n:t.pendingContext=n,t=i,(o=ii(r,o)).payload={element:e},null!==(t=void 0===t?null:t)&&(o.callback=t),li(l,o),Jl(l,r),r}function Hu(e,t,n,r){var o=t.current,i=Yl(),a=hi.suspense;return Vu(e,t,n,o=Ql(i,o,a),a,r)}function zu(e){if(!(e=e.current).child)return null;switch(e.child.tag){case 5:default:return e.child.stateNode}}function Ku(e){var t=1073741821-25*(1+((1073741821-Yl()+500)/25|0));t<=Zl&&--t,this._expirationTime=Zl=t,this._root=e,this._callbacks=this._next=null,this._hasChildren=this._didComplete=!1,this._children=null,this._defer=!0}function Wu(){this._callbacks=null,this._didCommit=!1,this._onCommit=this._onCommit.bind(this)}function Gu(e,t,n){var r=new Mu(e,t,n=null!=n&&!0===n.hydrate),o=Au(3,null,null,2===t?7:1===t?3:0);return r.current=o,o.stateNode=r,e[rr]=r.current,n&&0!==t&&function(e){var t=Sn(e);at.forEach(function(n){xn(n,e,t)}),lt.forEach(function(n){xn(n,e,t)})}(9===e.nodeType?e:e.ownerDocument),r}function qu(e,t,n){this._internalRoot=Gu(e,t,n)}function Xu(e,t){this._internalRoot=Gu(e,2,t)}function Yu(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||\" react-mount-point-unstable \"!==e.nodeValue))}function Qu(e,t,n,r,o){var i=n._reactRootContainer;if(i){var a=i._internalRoot;if(\"function\"===typeof o){var l=o;o=function(){var e=zu(a);l.call(e)}}Hu(t,a,e,o)}else{if(i=n._reactRootContainer=function(e,t){if(t||(t=!(!(t=e?9===e.nodeType?e.documentElement:e.firstChild:null)||1!==t.nodeType||!t.hasAttribute(\"data-reactroot\"))),!t)for(var n;n=e.lastChild;)e.removeChild(n);return new qu(e,0,t?{hydrate:!0}:void 0)}(n,r),a=i._internalRoot,\"function\"===typeof o){var u=o;o=function(){var e=zu(a);u.call(e)}}uu(function(){Hu(t,a,e,o)})}return zu(a)}function $u(e,t){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!Yu(t))throw a(Error(200));return function(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:M,key:null==r?null:\"\"+r,children:e,containerInfo:t,implementation:n}}(e,t,null,n)}J=function(e,t,n){switch(t){case\"input\":if(Ie(e,n),t=n.name,\"radio\"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll(\"input[name=\"+JSON.stringify(\"\"+t)+'][type=\"radio\"]'),t=0;t<n.length;t++){var r=n[t];if(r!==e&&r.form===e.form){var o=lr(r);if(!o)throw a(Error(90));Oe(r),Ie(r,o)}}}break;case\"textarea\":De(e,n);break;case\"select\":null!=(t=n.value)&&Ne(e,!!n.multiple,t,!1)}},Ku.prototype.render=function(e){if(!this._defer)throw a(Error(250));this._hasChildren=!0,this._children=e;var t=this._root._internalRoot,n=this._expirationTime,r=new Wu;return Vu(e,t,null,n,null,r._onCommit),r},Ku.prototype.then=function(e){if(this._didComplete)e();else{var t=this._callbacks;null===t&&(t=this._callbacks=[]),t.push(e)}},Ku.prototype.commit=function(){var e=this._root._internalRoot,t=e.firstBatch;if(!this._defer||null===t)throw a(Error(251));if(this._hasChildren){var n=this._expirationTime;if(t!==this){this._hasChildren&&(n=this._expirationTime=t._expirationTime,this.render(this._children));for(var r=null,o=t;o!==this;)r=o,o=o._next;if(null===r)throw a(Error(251));r._next=o._next,this._next=t,e.firstBatch=this}if(this._defer=!1,t=n,(wl&(yl|vl))!==hl)throw a(Error(253));Bu(e,t),nu(e),zo(),t=this._next,this._next=null,null!==(t=e.firstBatch=t)&&t._hasChildren&&t.render(t._children)}else this._next=null,this._defer=!1},Ku.prototype._onComplete=function(){if(!this._didComplete){this._didComplete=!0;var e=this._callbacks;if(null!==e)for(var t=0;t<e.length;t++)(0,e[t])()}},Wu.prototype.then=function(e){if(this._didCommit)e();else{var t=this._callbacks;null===t&&(t=this._callbacks=[]),t.push(e)}},Wu.prototype._onCommit=function(){if(!this._didCommit){this._didCommit=!0;var e=this._callbacks;if(null!==e)for(var t=0;t<e.length;t++){var n=e[t];if(\"function\"!==typeof n)throw a(Error(191),n);n()}}},Xu.prototype.render=qu.prototype.render=function(e,t){var n=this._internalRoot,r=new Wu;return null!==(t=void 0===t?null:t)&&r.then(t),Hu(e,n,null,r._onCommit),r},Xu.prototype.unmount=qu.prototype.unmount=function(e){var t=this._internalRoot,n=new Wu;return null!==(e=void 0===e?null:e)&&n.then(e),Hu(null,t,null,n._onCommit),n},Xu.prototype.createBatch=function(){var e=new Ku(this),t=e._expirationTime,n=this._internalRoot,r=n.firstBatch;if(null===r)n.firstBatch=e,e._next=null;else{for(n=null;null!==r&&r._expirationTime>=t;)n=r,r=r._next;e._next=r,null!==n&&(n._next=e)}return e},ie=au,ae=lu,le=ou,ue=function(e,t){var n=wl;wl|=2;try{return e(t)}finally{(wl=n)===hl&&zo()}};var Zu={createPortal:$u,findDOMNode:function(e){if(null==e)e=null;else if(1!==e.nodeType){var t=e._reactInternalFiber;if(void 0===t){if(\"function\"===typeof e.render)throw a(Error(188));throw a(Error(268),Object.keys(e))}e=null===(e=Et(t))?null:e.stateNode}return e},hydrate:function(e,t,n){if(!Yu(t))throw a(Error(200));return Qu(null,e,t,!0,n)},render:function(e,t,n){if(!Yu(t))throw a(Error(200));return Qu(null,e,t,!1,n)},unstable_renderSubtreeIntoContainer:function(e,t,n,r){if(!Yu(n))throw a(Error(200));if(null==e||void 0===e._reactInternalFiber)throw a(Error(38));return Qu(e,t,n,!1,r)},unmountComponentAtNode:function(e){if(!Yu(e))throw a(Error(40));return!!e._reactRootContainer&&(uu(function(){Qu(null,null,e,!1,function(){e._reactRootContainer=null})}),!0)},unstable_createPortal:function(){return $u.apply(void 0,arguments)},unstable_batchedUpdates:au,unstable_interactiveUpdates:function(e,t,n,r){return ou(),lu(e,t,n,r)},unstable_discreteUpdates:lu,unstable_flushDiscreteUpdates:ou,flushSync:function(e,t){if((wl&(yl|vl))!==hl)throw a(Error(187));var n=wl;wl|=1;try{return Bo(99,e.bind(null,t))}finally{wl=n,zo()}},unstable_createRoot:function(e,t){if(!Yu(e))throw a(Error(299),\"unstable_createRoot\");return new Xu(e,t)},unstable_createSyncRoot:function(e,t){if(!Yu(e))throw a(Error(299),\"unstable_createRoot\");return new qu(e,1,t)},unstable_flushControlled:function(e){var t=wl;wl|=1;try{Bo(99,e)}finally{(wl=t)===hl&&zo()}},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{Events:[ir,ar,lr,k.injectEventPluginsByName,d,It,function(e){S(e,xt)},re,oe,Tn,A,Eu,{current:!1}]}};!function(e){var t=e.findFiberByHostInstance;(function(e){if(\"undefined\"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);Su=function(e){try{t.onCommitFiberRoot(n,e,void 0,64===(64&e.current.effectTag))}catch(r){}},xu=function(e){try{t.onCommitFiberUnmount(n,e)}catch(r){}}}catch(r){}})(o({},e,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:N.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Et(e))?null:e.stateNode},findFiberByHostInstance:function(e){return t?t(e):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}))}({findFiberByHostInstance:or,bundleType:0,version:\"16.10.1\",rendererPackageName:\"react-dom\"});var Ju={default:Zu},es=Ju&&Zu||Ju;e.exports=es.default||es},function(e,t,n){\"use strict\";e.exports=n(51)},function(e,t,n){\"use strict\";var r,o,i,a,l;if(Object.defineProperty(t,\"__esModule\",{value:!0}),\"undefined\"===typeof window||\"function\"!==typeof MessageChannel){var u=null,s=null,c=function e(){if(null!==u)try{var n=t.unstable_now();u(!0,n),u=null}catch(r){throw setTimeout(e,0),r}},f=Date.now();t.unstable_now=function(){return Date.now()-f},r=function(e){null!==u?setTimeout(r,0,e):(u=e,setTimeout(c,0))},o=function(e,t){s=setTimeout(e,t)},i=function(){clearTimeout(s)},a=function(){return!1},l=t.unstable_forceFrameRate=function(){}}else{var d=window.performance,p=window.Date,h=window.setTimeout,m=window.clearTimeout,y=window.requestAnimationFrame,v=window.cancelAnimationFrame;if(\"undefined\"!==typeof console&&(\"function\"!==typeof y&&console.error(\"This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills\"),\"function\"!==typeof v&&console.error(\"This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills\")),\"object\"===typeof d&&\"function\"===typeof d.now)t.unstable_now=function(){return d.now()};else{var g=p.now();t.unstable_now=function(){return p.now()-g}}var b=!1,_=null,E=-1,T=5,C=0;a=function(){return t.unstable_now()>=C},l=function(){},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error(\"forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported\"):T=0<e?Math.floor(1e3/e):33.33};var O=new MessageChannel,w=O.port2;O.port1.onmessage=function(){if(null!==_){var e=t.unstable_now();C=e+T;try{_(!0,e)?w.postMessage(null):(b=!1,_=null)}catch(n){throw w.postMessage(null),n}}else b=!1},r=function(e){_=e,b||(b=!0,w.postMessage(null))},o=function(e,n){E=h(function(){e(t.unstable_now())},n)},i=function(){m(E),E=-1}}function S(e,t){var n=e.length;e.push(t);e:for(;;){var r=Math.floor((n-1)/2),o=e[r];if(!(void 0!==o&&0<A(o,t)))break e;e[r]=t,e[n]=o,n=r}}function x(e){return void 0===(e=e[0])?null:e}function I(e){var t=e[0];if(void 0!==t){var n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,o=e.length;r<o;){var i=2*(r+1)-1,a=e[i],l=i+1,u=e[l];if(void 0!==a&&0>A(a,n))void 0!==u&&0>A(u,a)?(e[r]=u,e[l]=n,r=l):(e[r]=a,e[i]=n,r=i);else{if(!(void 0!==u&&0>A(u,n)))break e;e[r]=u,e[l]=n,r=l}}}return t}return null}function A(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var k=[],P=[],N=1,L=null,R=3,D=!1,M=!1,j=!1;function F(e){for(var t=x(P);null!==t;){if(null===t.callback)I(P);else{if(!(t.startTime<=e))break;I(P),t.sortIndex=t.expirationTime,S(k,t)}t=x(P)}}function U(e){if(j=!1,F(e),!M)if(null!==x(k))M=!0,r(B);else{var t=x(P);null!==t&&o(U,t.startTime-e)}}function B(e,n){M=!1,j&&(j=!1,i()),D=!0;var r=R;try{for(F(n),L=x(k);null!==L&&(!(L.expirationTime>n)||e&&!a());){var l=L.callback;if(null!==l){L.callback=null,R=L.priorityLevel;var u=l(L.expirationTime<=n);n=t.unstable_now(),\"function\"===typeof u?L.callback=u:L===x(k)&&I(k),F(n)}else I(k);L=x(k)}if(null!==L)var s=!0;else{var c=x(P);null!==c&&o(U,c.startTime-n),s=!1}return s}finally{L=null,R=r,D=!1}}function V(e){switch(e){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var H=l;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=R;R=e;try{return t()}finally{R=n}},t.unstable_next=function(e){switch(R){case 1:case 2:case 3:var t=3;break;default:t=R}var n=R;R=t;try{return e()}finally{R=n}},t.unstable_scheduleCallback=function(e,n,a){var l=t.unstable_now();if(\"object\"===typeof a&&null!==a){var u=a.delay;u=\"number\"===typeof u&&0<u?l+u:l,a=\"number\"===typeof a.timeout?a.timeout:V(e)}else a=V(e),u=l;return e={id:N++,callback:n,priorityLevel:e,startTime:u,expirationTime:a=u+a,sortIndex:-1},u>l?(e.sortIndex=u,S(P,e),null===x(k)&&e===x(P)&&(j?i():j=!0,o(U,u-l))):(e.sortIndex=a,S(k,e),M||D||(M=!0,r(B))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=R;return function(){var n=R;R=t;try{return e.apply(this,arguments)}finally{R=n}}},t.unstable_getCurrentPriorityLevel=function(){return R},t.unstable_shouldYield=function(){var e=t.unstable_now();F(e);var n=x(k);return n!==L&&null!==L&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime<L.expirationTime||a()},t.unstable_requestPaint=H,t.unstable_continueExecution=function(){M||D||(M=!0,r(B))},t.unstable_pauseExecution=function(){},t.unstable_getFirstCallbackNode=function(){return x(k)},t.unstable_Profiling=null},,function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){},function(e,t,n){var r=function(e){\"use strict\";var t,n=Object.prototype,r=n.hasOwnProperty,o=\"function\"===typeof Symbol?Symbol:{},i=o.iterator||\"@@iterator\",a=o.asyncIterator||\"@@asyncIterator\",l=o.toStringTag||\"@@toStringTag\";function u(e,t,n,r){var o=t&&t.prototype instanceof m?t:m,i=Object.create(o.prototype),a=new x(r||[]);return i._invoke=function(e,t,n){var r=c;return function(o,i){if(r===d)throw new Error(\"Generator is already running\");if(r===p){if(\"throw\"===o)throw i;return A()}for(n.method=o,n.arg=i;;){var a=n.delegate;if(a){var l=O(a,n);if(l){if(l===h)continue;return l}}if(\"next\"===n.method)n.sent=n._sent=n.arg;else if(\"throw\"===n.method){if(r===c)throw r=p,n.arg;n.dispatchException(n.arg)}else\"return\"===n.method&&n.abrupt(\"return\",n.arg);r=d;var u=s(e,t,n);if(\"normal\"===u.type){if(r=n.done?p:f,u.arg===h)continue;return{value:u.arg,done:n.done}}\"throw\"===u.type&&(r=p,n.method=\"throw\",n.arg=u.arg)}}}(e,n,a),i}function s(e,t,n){try{return{type:\"normal\",arg:e.call(t,n)}}catch(r){return{type:\"throw\",arg:r}}}e.wrap=u;var c=\"suspendedStart\",f=\"suspendedYield\",d=\"executing\",p=\"completed\",h={};function m(){}function y(){}function v(){}var g={};g[i]=function(){return this};var b=Object.getPrototypeOf,_=b&&b(b(I([])));_&&_!==n&&r.call(_,i)&&(g=_);var E=v.prototype=m.prototype=Object.create(g);function T(e){[\"next\",\"throw\",\"return\"].forEach(function(t){e[t]=function(e){return this._invoke(t,e)}})}function C(e){var t;this._invoke=function(n,o){function i(){return new Promise(function(t,i){!function t(n,o,i,a){var l=s(e[n],e,o);if(\"throw\"!==l.type){var u=l.arg,c=u.value;return c&&\"object\"===typeof c&&r.call(c,\"__await\")?Promise.resolve(c.__await).then(function(e){t(\"next\",e,i,a)},function(e){t(\"throw\",e,i,a)}):Promise.resolve(c).then(function(e){u.value=e,i(u)},function(e){return t(\"throw\",e,i,a)})}a(l.arg)}(n,o,t,i)})}return t=t?t.then(i,i):i()}}function O(e,n){var r=e.iterator[n.method];if(r===t){if(n.delegate=null,\"throw\"===n.method){if(e.iterator.return&&(n.method=\"return\",n.arg=t,O(e,n),\"throw\"===n.method))return h;n.method=\"throw\",n.arg=new TypeError(\"The iterator does not provide a 'throw' method\")}return h}var o=s(r,e.iterator,n.arg);if(\"throw\"===o.type)return n.method=\"throw\",n.arg=o.arg,n.delegate=null,h;var i=o.arg;return i?i.done?(n[e.resultName]=i.value,n.next=e.nextLoc,\"return\"!==n.method&&(n.method=\"next\",n.arg=t),n.delegate=null,h):i:(n.method=\"throw\",n.arg=new TypeError(\"iterator result is not an object\"),n.delegate=null,h)}function w(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function S(e){var t=e.completion||{};t.type=\"normal\",delete t.arg,e.completion=t}function x(e){this.tryEntries=[{tryLoc:\"root\"}],e.forEach(w,this),this.reset(!0)}function I(e){if(e){var n=e[i];if(n)return n.call(e);if(\"function\"===typeof e.next)return e;if(!isNaN(e.length)){var o=-1,a=function n(){for(;++o<e.length;)if(r.call(e,o))return n.value=e[o],n.done=!1,n;return n.value=t,n.done=!0,n};return a.next=a}}return{next:A}}function A(){return{value:t,done:!0}}return y.prototype=E.constructor=v,v.constructor=y,v[l]=y.displayName=\"GeneratorFunction\",e.isGeneratorFunction=function(e){var t=\"function\"===typeof e&&e.constructor;return!!t&&(t===y||\"GeneratorFunction\"===(t.displayName||t.name))},e.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,v):(e.__proto__=v,l in e||(e[l]=\"GeneratorFunction\")),e.prototype=Object.create(E),e},e.awrap=function(e){return{__await:e}},T(C.prototype),C.prototype[a]=function(){return this},e.AsyncIterator=C,e.async=function(t,n,r,o){var i=new C(u(t,n,r,o));return e.isGeneratorFunction(n)?i:i.next().then(function(e){return e.done?e.value:i.next()})},T(E),E[l]=\"Generator\",E[i]=function(){return this},E.toString=function(){return\"[object Generator]\"},e.keys=function(e){var t=[];for(var n in e)t.push(n);return t.reverse(),function n(){for(;t.length;){var r=t.pop();if(r in e)return n.value=r,n.done=!1,n}return n.done=!0,n}},e.values=I,x.prototype={constructor:x,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=t,this.done=!1,this.delegate=null,this.method=\"next\",this.arg=t,this.tryEntries.forEach(S),!e)for(var n in this)\"t\"===n.charAt(0)&&r.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=t)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if(\"throw\"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var n=this;function o(r,o){return l.type=\"throw\",l.arg=e,n.next=r,o&&(n.method=\"next\",n.arg=t),!!o}for(var i=this.tryEntries.length-1;i>=0;--i){var a=this.tryEntries[i],l=a.completion;if(\"root\"===a.tryLoc)return o(\"end\");if(a.tryLoc<=this.prev){var u=r.call(a,\"catchLoc\"),s=r.call(a,\"finallyLoc\");if(u&&s){if(this.prev<a.catchLoc)return o(a.catchLoc,!0);if(this.prev<a.finallyLoc)return o(a.finallyLoc)}else if(u){if(this.prev<a.catchLoc)return o(a.catchLoc,!0)}else{if(!s)throw new Error(\"try statement without catch or finally\");if(this.prev<a.finallyLoc)return o(a.finallyLoc)}}}},abrupt:function(e,t){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,\"finallyLoc\")&&this.prev<o.finallyLoc){var i=o;break}}i&&(\"break\"===e||\"continue\"===e)&&i.tryLoc<=t&&t<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=e,a.arg=t,i?(this.method=\"next\",this.next=i.finallyLoc,h):this.complete(a)},complete:function(e,t){if(\"throw\"===e.type)throw e.arg;return\"break\"===e.type||\"continue\"===e.type?this.next=e.arg:\"return\"===e.type?(this.rval=this.arg=e.arg,this.method=\"return\",this.next=\"end\"):\"normal\"===e.type&&t&&(this.next=t),h},finish:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),S(n),h}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if(\"throw\"===r.type){var o=r.arg;S(n)}return o}}throw new Error(\"illegal catch attempt\")},delegateYield:function(e,n,r){return this.delegate={iterator:I(e),resultName:n,nextLoc:r},\"next\"===this.method&&(this.arg=t),h}},e}(e.exports);try{regeneratorRuntime=r}catch(o){Function(\"r\",\"regeneratorRuntime = r\")(r)}},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r,o=n(0),i=(r=n(60))&&r.__esModule?r:{default:r};var a=(0,o.memo)(i.default);t.default=a},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t=f();if(t&&t.has(e))return t.get(e);var n={};if(null!=e){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}}n.default=e,t&&t.set(e,n);return n}(n(0)),o=c(n(3)),i=c(n(29)),a=n(16),l=n(30),u=c(n(31)),s=c(n(70));function c(e){return e&&e.__esModule?e:{default:e}}function f(){if(\"function\"!==typeof WeakMap)return null;var e=new WeakMap;return f=function(){return e},e}function d(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function p(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?d(n,!0).forEach(function(t){h(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):d(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function h(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e)))return;var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(u){o=!0,i=u}finally{try{r||null==l.return||l.return()}finally{if(o)throw i}}return n}(e,t)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}()}var y=function(e){var t=e.value,n=e.language,o=e.editorDidMount,c=e.theme,f=e.line,d=e.width,h=e.height,y=e.loading,v=e.options,g=m((0,r.useState)(!1),2),b=g[0],_=g[1],E=m((0,r.useState)(!0),2),T=E[0],C=E[1],O=(0,r.useRef)(),w=(0,r.useRef)(),S=(0,r.useRef)();(0,l.useMount)(function(e){return a.monaco.init().then(function(e){return(w.current=e)&&C(!1)}).catch(function(e){return console.error(\"An error occurred during initialization of Monaco: \",e)}),I}),(0,l.useUpdate)(function(e){v.readOnly?O.current.setValue(t):(O.current.executeEdits(\"\",[{range:O.current.getModel().getFullModelRange(),text:t}]),O.current.pushUndoStop())},[t],b),(0,l.useUpdate)(function(e){O.current.setValue(t),w.current.editor.setModelLanguage(O.current.getModel(),n)},[n],b),(0,l.useUpdate)(function(e){O.current.setScrollPosition({scrollTop:f})},[f],b),(0,l.useUpdate)(function(e){w.current.editor.setTheme(c)},[c],b),(0,l.useUpdate)(function(e){O.current.updateOptions(v)},[v],b);var x=(0,r.useCallback)(function(e){O.current=w.current.editor.create(S.current,p({value:t,language:n,automaticLayout:!0},v)),o(O.current.getValue.bind(O.current),O.current),w.current.editor.defineTheme(\"dark\",u.default[\"night-dark\"]),w.current.editor.setTheme(c),_(!0)},[o,n,v,c,t]);(0,r.useEffect)(function(e){!T&&!b&&x()},[T,b,x]);var I=function(e){return O.current&&O.current.dispose()};return r.default.createElement(\"section\",{style:p({},s.default.wrapper,{width:d,height:h})},!b&&r.default.createElement(i.default,{content:y}),r.default.createElement(\"div\",{ref:S,style:p({},s.default.fullWidth,{},!b&&s.default.hide)}))};y.propTypes={value:o.default.string,language:o.default.string,editorDidMount:o.default.func,theme:o.default.string,line:o.default.number,width:o.default.oneOfType([o.default.number,o.default.string]),height:o.default.oneOfType([o.default.number,o.default.string]),loading:o.default.oneOfType([o.default.element,o.default.string]),options:o.default.object},y.defaultProps={editorDidMount:a.noop,theme:\"light\",width:\"100%\",height:\"100%\",loading:\"Loading...\",options:{}};var v=y;t.default=v},function(e,t,n){\"use strict\";var r=n(62);function o(){}function i(){}i.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,i,a){if(a!==r){var l=new Error(\"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types\");throw l.name=\"Invariant Violation\",l}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:o};return n.PropTypes=n,n}},function(e,t,n){\"use strict\";e.exports=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\"},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r,o=(r=n(0))&&r.__esModule?r:{default:r};var i={display:\"flex\",height:\"100%\",width:\"100%\",justifyContent:\"center\",alignItems:\"center\"};var a=function(e){var t=e.content;return o.default.createElement(\"div\",{style:i},t)};t.default=a},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r,o=(r=n(65))&&r.__esModule?r:{default:r},i=n(16);function a(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var u=new(function(){function e(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),l(this,\"handleMainScriptLoad\",function(e){document.removeEventListener(\"monaco_init\",t.handleMainScriptLoad),t.resolve(window.monaco)}),l(this,\"isInitialized\",!1),l(this,\"wrapperPromise\",new Promise(function(e,n){t.resolve=e,t.reject=n})),this.__config=n}var t,n,r;return t=e,(n=[{key:\"config\",value:function(e){return e&&(this.__config=(0,i.deepMerge)(this.__config,e)),this}},{key:\"injectScripts\",value:function(e){document.body.appendChild(e)}},{key:\"createScript\",value:function(e){var t=document.createElement(\"script\");return e&&(t.src=e),t}},{key:\"createMonacoLoaderScript\",value:function(e){var t=this,n=this.createScript(this.__config.urls.monacoLoader);return n.onload=function(n){return t.injectScripts(e)},n.onerror=this.reject,n}},{key:\"createMainScript\",value:function(){var e=this.createScript();return e.innerHTML=\"\\n      require.config({ paths: { 'vs': '\".concat(this.__config.urls.monacoBase,\"' } });\\n      require(['vs/editor/editor.main'], function() {\\n        document.dispatchEvent(new Event('monaco_init'));\\n      });\\n    \"),e.onerror=this.reject,e}},{key:\"init\",value:function(){if(!this.isInitialized){if(window.monaco&&window.monaco.editor)return new Promise(function(e,t){return e(window.monaco)});document.addEventListener(\"monaco_init\",this.handleMainScriptLoad);var e=this.createMainScript(),t=this.createMonacoLoaderScript(e);this.injectScripts(t)}return this.isInitialized=!0,this.wrapperPromise}}])&&a(t.prototype,n),r&&a(t,r),e}())(o.default);t.default=u},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r={urls:{monacoLoader:\"https://cdn.jsdelivr.net/npm/monaco-editor@0.18.1/min/vs/loader.js\",monacoBase:\"https://cdn.jsdelivr.net/npm/monaco-editor@0.18.1/min/vs\"}};t.default=r},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=function(e){};t.default=r},function(e,t,n){\"use strict\";function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var i=function e(t,n){return Object.keys(n).forEach(function(r){n[r]instanceof Object&&Object.assign(n[r],e(t[r],n[r]))}),function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(n,!0).forEach(function(t){o(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}({},t,{},n)};t.default=i},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=n(0),o=function(e){return(0,r.useEffect)(e,[])};t.default=o},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=n(0),o=function(e,t){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=(0,r.useRef)(!0);(0,r.useEffect)(o.current||!n?function(e){o.current=!1}:e,t)};t.default=o},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r={wrapper:{display:\"flex\",position:\"relative\",textAlign:\"initial\"},fullWidth:{width:\"100%\"},hide:{display:\"none\"}};t.default=r},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r,o=n(0),i=(r=n(72))&&r.__esModule?r:{default:r};var a=(0,o.memo)(i.default);t.default=a},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t=f();if(t&&t.has(e))return t.get(e);var n={};if(null!=e){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}}n.default=e,t&&t.set(e,n);return n}(n(0)),o=c(n(3)),i=c(n(29)),a=n(16),l=n(30),u=c(n(31)),s=c(n(73));function c(e){return e&&e.__esModule?e:{default:e}}function f(){if(\"function\"!==typeof WeakMap)return null;var e=new WeakMap;return f=function(){return e},e}function d(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function p(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?d(n,!0).forEach(function(t){h(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):d(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function h(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e)))return;var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(u){o=!0,i=u}finally{try{r||null==l.return||l.return()}finally{if(o)throw i}}return n}(e,t)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}()}var y=function(e){var t=e.original,n=e.modified,o=e.language,c=e.originalLanguage,f=e.modifiedLanguage,d=e.editorDidMount,h=e.theme,y=e.line,v=e.width,g=e.height,b=e.loading,_=e.options,E=m((0,r.useState)(!1),2),T=E[0],C=E[1],O=m((0,r.useState)(!0),2),w=O[0],S=O[1],x=(0,r.useRef)(),I=(0,r.useRef)(),A=(0,r.useRef)();(0,l.useMount)(function(e){return a.monaco.init().then(function(e){return(I.current=e)&&S(!1)}).catch(function(e){return console.error(\"An error occurred during initialization of Monaco: \",e)}),N}),(0,l.useUpdate)(function(e){x.current.getModel().modified.setValue(n)},[n],T),(0,l.useUpdate)(function(e){x.current.getModel().original.setValue(t)},[t],T),(0,l.useUpdate)(function(e){var t=x.current.getModel(),n=t.original,r=t.modified;I.current.editor.setModelLanguage(n,c||o),I.current.editor.setModelLanguage(r,f||o)},[o,c,f],T),(0,l.useUpdate)(function(e){x.current.setScrollPosition({scrollTop:y})},[y],T),(0,l.useUpdate)(function(e){I.current.editor.setTheme(h)},[h],T),(0,l.useUpdate)(function(e){x.current.updateOptions(_)},[_],T);var k=(0,r.useCallback)(function(e){var r=I.current.editor.createModel(t,c||o),i=I.current.editor.createModel(n,f||o);x.current.setModel({original:r,modified:i})},[o,n,f,t,c]),P=(0,r.useCallback)(function(e){x.current=I.current.editor.createDiffEditor(A.current,p({automaticLayout:!0},_)),k();var t=x.current.getModel(),n=t.original,r=t.modified;d(r.getValue.bind(r),n.getValue.bind(n),x.current),I.current.editor.defineTheme(\"dark\",u.default[\"night-dark\"]),I.current.editor.setTheme(h),C(!0)},[d,_,h,k]);(0,r.useEffect)(function(e){!w&&!T&&P()},[w,T,P]);var N=function(e){return x.current&&x.current.dispose()};return r.default.createElement(\"section\",{style:p({},s.default.wrapper,{width:v,height:g})},!T&&r.default.createElement(i.default,{content:b}),r.default.createElement(\"div\",{ref:A,style:p({},s.default.fullWidth,{},!T&&s.default.hide)}))};y.propTypes={original:o.default.string,modified:o.default.string,language:o.default.string,originalLanguage:o.default.string,modifiedLanguage:o.default.string,editorDidMount:o.default.func,theme:o.default.string,line:o.default.number,width:o.default.oneOfType([o.default.number,o.default.string]),height:o.default.oneOfType([o.default.number,o.default.string]),loading:o.default.oneOfType([o.default.element,o.default.string]),options:o.default.object},y.defaultProps={editorDidMount:a.noop,theme:\"light\",width:\"100%\",height:\"100%\",loading:\"Loading...\",options:{}};var v=y;t.default=v},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r={wrapper:{display:\"flex\",position:\"relative\",textAlign:\"initial\"},fullWidth:{width:\"100%\"},hide:{display:\"none\"}};t.default=r},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r,o=n(0),i=(r=n(75))&&r.__esModule?r:{default:r};var a=(0,o.memo)(i.default);t.default=a},function(e,t,n){\"use strict\";Object.defineProperty(t,\"__esModule\",{value:!0}),t.default=void 0;var r=l(n(0)),o=l(n(3)),i=l(n(10)),a=n(16);function l(e){return e&&e.__esModule?e:{default:e}}function u(){return(u=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function s(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=function(e){var t=e.value,n=e.onChange,o=e.editorDidMount,a=s(e,[\"value\",\"onChange\",\"editorDidMount\"]);return r.default.createElement(i.default,u({value:t,editorDidMount:function(e,t){t.onDidChangeModelContent(function(e){var r=t.getValue(),o=n(e,r);\"string\"===typeof o&&r!==o&&t.setValue(o)}),o(e,t)}},a))};c.propTypes={value:o.default.string,editorDidMount:o.default.func,onChange:o.default.func},c.defaultProps={editorDidMount:a.noop,onChange:a.noop};var f=c;t.default=f},function(e,t){var n=[\"input\",\"select\",\"textarea\",\"a[href]\",\"button\",\"[tabindex]\",\"audio[controls]\",\"video[controls]\",'[contenteditable]:not([contenteditable=\"false\"])'],r=n.join(\",\"),o=\"undefined\"===typeof Element?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector;function i(e,t){t=t||{};var n,i,l,u=[],f=[],d=e.querySelectorAll(r);for(t.includeContainer&&o.call(e,r)&&(d=Array.prototype.slice.apply(d)).unshift(e),n=0;n<d.length;n++)a(i=d[n])&&(0===(l=s(i))?u.push(i):f.push({documentOrder:n,tabIndex:l,node:i}));return f.sort(c).map(function(e){return e.node}).concat(u)}function a(e){return!(!l(e)||function(e){return function(e){return f(e)&&\"radio\"===e.type}(e)&&!function(e){if(!e.name)return!0;var t=function(e){for(var t=0;t<e.length;t++)if(e[t].checked)return e[t]}(e.ownerDocument.querySelectorAll('input[type=\"radio\"][name=\"'+e.name+'\"]'));return!t||t===e}(e)}(e)||s(e)<0)}function l(e){return!(e.disabled||function(e){return f(e)&&\"hidden\"===e.type}(e)||function(e){return null===e.offsetParent||\"hidden\"===getComputedStyle(e).visibility}(e))}i.isTabbable=function(e){if(!e)throw new Error(\"No node provided\");return!1!==o.call(e,r)&&a(e)},i.isFocusable=function(e){if(!e)throw new Error(\"No node provided\");return!1!==o.call(e,u)&&l(e)};var u=n.concat(\"iframe\").join(\",\");function s(e){var t=parseInt(e.getAttribute(\"tabindex\"),10);return isNaN(t)?function(e){return\"true\"===e.contentEditable}(e)?0:e.tabIndex:t}function c(e,t){return e.tabIndex===t.tabIndex?e.documentOrder-t.documentOrder:e.tabIndex-t.tabIndex}function f(e){return\"INPUT\"===e.tagName}e.exports=i},function(e,t){e.exports=function(){for(var e={},t=0;t<arguments.length;t++){var r=arguments[t];for(var o in r)n.call(r,o)&&(e[o]=r[o])}return e};var n=Object.prototype.hasOwnProperty},function(e,t){var n,r,o=e.exports={};function i(){throw new Error(\"setTimeout has not been defined\")}function a(){throw new Error(\"clearTimeout has not been defined\")}function l(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n=\"function\"===typeof setTimeout?setTimeout:i}catch(e){n=i}try{r=\"function\"===typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var u,s=[],c=!1,f=-1;function d(){c&&u&&(c=!1,u.length?s=u.concat(s):f=-1,s.length&&p())}function p(){if(!c){var e=l(d);c=!0;for(var t=s.length;t;){for(u=s,s=[];++f<t;)u&&u[f].run();f=-1,t=s.length}u=null,c=!1,function(e){if(r===clearTimeout)return clearTimeout(e);if((r===a||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(e);try{r(e)}catch(t){try{return r.call(null,e)}catch(t){return r.call(this,e)}}}(e)}}function h(e,t){this.fun=e,this.array=t}function m(){}o.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];s.push(new h(e,t)),1!==s.length||c||l(p)},h.prototype.run=function(){this.fun.apply(null,this.array)},o.title=\"browser\",o.browser=!0,o.env={},o.argv=[],o.version=\"\",o.versions={},o.on=m,o.addListener=m,o.once=m,o.off=m,o.removeListener=m,o.removeAllListeners=m,o.emit=m,o.prependListener=m,o.prependOnceListener=m,o.listeners=function(e){return[]},o.binding=function(e){throw new Error(\"process.binding is not supported\")},o.cwd=function(){return\"/\"},o.chdir=function(e){throw new Error(\"process.chdir is not supported\")},o.umask=function(){return 0}},function(e,t,n){},,,function(e,t,n){},,,,,,,,,,,,,,,,function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return r}),n.d(t,\"b\",function(){return o});var r=function(e,t){if(e instanceof Element){if(e&&e.closest)return e.closest(t);for(var n=e;n;){if(o(n,t))return n;n=n.parentElement}}return null},o=function(e,t){return(e.matches||e.webkitMatchesSelector||e.msMatchesSelector).call(e,t)}},function(e,t,n){\"use strict\";n.d(t,\"d\",function(){return c}),n.d(t,\"b\",function(){return f}),n.d(t,\"c\",function(){return d}),n.d(t,\"a\",function(){return p});var r=n(0),o=n(6),i=n(2),a=n(7),l=n(5),u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},s=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},c=Object(a.b)({surface:!1})(Object(o.a)({displayName:\"ListItem\",defaultProps:{tabIndex:0},classNames:function(e){return[\"mdc-list-item\",{\"mdc-list-item--selected\":e.selected,\"mdc-list-item--activated\":e.activated,\"mdc-list-item--disabled\":e.disabled}]},consumeProps:[\"selected\",\"activated\",\"disabled\",\"options\"]})),f=(Object(o.a)({displayName:\"ListItemText\",tag:\"span\",classNames:[\"mdc-list-item__text\"]}),Object(o.a)({displayName:\"ListItemPrimaryText\",tag:\"span\",classNames:[\"mdc-list-item__primary-text\"]}),Object(o.a)({displayName:\"ListItemSecondaryText\",tag:\"span\",classNames:[\"mdc-list-item__secondary-text\"]}),Object(o.a)({displayName:\"ListItemGraphic\",classNames:[\"mdc-list-item__graphic\"],tag:l.a}),Object(o.a)({displayName:\"ListItemMeta\",classNames:[\"mdc-list-item__meta\"],tag:\"div\",render:function(e,t,n){if(e.icon)return r.createElement(l.a,u({ref:t},e));if(r.isValidElement(e.children)){e.children;var o=s(e,[\"children\"]);return r.cloneElement(e.children,u({},o,e.children.props,{className:Object(i.a)(e.className,e.children.props.className)}))}return r.createElement(n,u({ref:t},e))}}),Object(o.a)({displayName:\"ListGroup\",classNames:[\"mdc-list-group\"]})),d=Object(o.a)({displayName:\"ListGroupSubheader\",classNames:[\"mdc-list-group__subheader\"]}),p=Object(o.a)({displayName:\"ListDivider\",classNames:[\"mdc-list-divider\"]})},function(e,t,n){\"use strict\";n.d(t,\"a\",function(){return r});var r=function(e){return e+\"-\"+(Math.random()+Math.random()+1).toString(36).substring(2)}},function(e,t,n){\"use strict\";var r={};n.r(r),n.d(r,\"computeHorizontalScrollbarHeight\",function(){return T});var o=n(0),i=n(6),a=n(36),l=n(1),u=n(11),s={ARROW_LEFT_KEY:\"ArrowLeft\",ARROW_RIGHT_KEY:\"ArrowRight\",END_KEY:\"End\",ENTER_KEY:\"Enter\",HOME_KEY:\"Home\",SPACE_KEY:\"Space\",TAB_ACTIVATED_EVENT:\"MDCTabBar:activated\",TAB_SCROLLER_SELECTOR:\".mdc-tab-scroller\",TAB_SELECTOR:\".mdc-tab\"},c={ARROW_LEFT_KEYCODE:37,ARROW_RIGHT_KEYCODE:39,END_KEYCODE:35,ENTER_KEYCODE:13,EXTRA_SCROLL_AMOUNT:20,HOME_KEYCODE:36,SPACE_KEYCODE:32},f=new Set;f.add(s.ARROW_LEFT_KEY),f.add(s.ARROW_RIGHT_KEY),f.add(s.END_KEY),f.add(s.HOME_KEY),f.add(s.ENTER_KEY),f.add(s.SPACE_KEY);var d=new Map;d.set(c.ARROW_LEFT_KEYCODE,s.ARROW_LEFT_KEY),d.set(c.ARROW_RIGHT_KEYCODE,s.ARROW_RIGHT_KEY),d.set(c.END_KEYCODE,s.END_KEY),d.set(c.HOME_KEYCODE,s.HOME_KEY),d.set(c.ENTER_KEYCODE,s.ENTER_KEY),d.set(c.SPACE_KEYCODE,s.SPACE_KEY);var p,h=function(e){function t(n){var r=e.call(this,l.a({},t.defaultAdapter,n))||this;return r.useAutomaticActivation_=!1,r}return l.b(t,e),Object.defineProperty(t,\"strings\",{get:function(){return s},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return c},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{scrollTo:function(){},incrementScroll:function(){},getScrollPosition:function(){return 0},getScrollContentWidth:function(){return 0},getOffsetWidth:function(){return 0},isRTL:function(){return!1},setActiveTab:function(){},activateTabAtIndex:function(){},deactivateTabAtIndex:function(){},focusTabAtIndex:function(){},getTabIndicatorClientRectAtIndex:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},getTabDimensionsAtIndex:function(){return{rootLeft:0,rootRight:0,contentLeft:0,contentRight:0}},getPreviousActiveTabIndex:function(){return-1},getFocusedTabIndex:function(){return-1},getIndexOfTabById:function(){return-1},getTabListLength:function(){return 0},notifyTabActivated:function(){}}},enumerable:!0,configurable:!0}),t.prototype.setUseAutomaticActivation=function(e){this.useAutomaticActivation_=e},t.prototype.activateTab=function(e){var t,n=this.adapter_.getPreviousActiveTabIndex();this.indexIsInRange_(e)&&e!==n&&(-1!==n&&(this.adapter_.deactivateTabAtIndex(n),t=this.adapter_.getTabIndicatorClientRectAtIndex(n)),this.adapter_.activateTabAtIndex(e,t),this.scrollIntoView(e),this.adapter_.notifyTabActivated(e))},t.prototype.handleKeyDown=function(e){var t=this.getKeyFromEvent_(e);if(void 0!==t)if(this.isActivationKey_(t)||e.preventDefault(),this.useAutomaticActivation_){if(this.isActivationKey_(t))return;var n=this.determineTargetFromKey_(this.adapter_.getPreviousActiveTabIndex(),t);this.adapter_.setActiveTab(n),this.scrollIntoView(n)}else{var r=this.adapter_.getFocusedTabIndex();if(this.isActivationKey_(t))this.adapter_.setActiveTab(r);else{n=this.determineTargetFromKey_(r,t);this.adapter_.focusTabAtIndex(n),this.scrollIntoView(n)}}},t.prototype.handleTabInteraction=function(e){this.adapter_.setActiveTab(this.adapter_.getIndexOfTabById(e.detail.tabId))},t.prototype.scrollIntoView=function(e){if(this.indexIsInRange_(e))return 0===e?this.adapter_.scrollTo(0):e===this.adapter_.getTabListLength()-1?this.adapter_.scrollTo(this.adapter_.getScrollContentWidth()):this.isRTL_()?this.scrollIntoViewRTL_(e):void this.scrollIntoView_(e)},t.prototype.determineTargetFromKey_=function(e,t){var n=this.isRTL_(),r=this.adapter_.getTabListLength()-1,o=e;return t===s.END_KEY?o=r:t===s.ARROW_LEFT_KEY&&!n||t===s.ARROW_RIGHT_KEY&&n?o-=1:t===s.ARROW_RIGHT_KEY&&!n||t===s.ARROW_LEFT_KEY&&n?o+=1:o=0,o<0?o=r:o>r&&(o=0),o},t.prototype.calculateScrollIncrement_=function(e,t,n,r){var o=this.adapter_.getTabDimensionsAtIndex(t),i=o.contentLeft-n-r,a=o.contentRight-n-c.EXTRA_SCROLL_AMOUNT,l=i+c.EXTRA_SCROLL_AMOUNT;return t<e?Math.min(a,0):Math.max(l,0)},t.prototype.calculateScrollIncrementRTL_=function(e,t,n,r,o){var i=this.adapter_.getTabDimensionsAtIndex(t),a=o-i.contentLeft-n,l=o-i.contentRight-n-r+c.EXTRA_SCROLL_AMOUNT,u=a-c.EXTRA_SCROLL_AMOUNT;return t>e?Math.max(l,0):Math.min(u,0)},t.prototype.findAdjacentTabIndexClosestToEdge_=function(e,t,n,r){var o=t.rootLeft-n,i=t.rootRight-n-r,a=o+i;return o<0||a<0?e-1:i>0||a>0?e+1:-1},t.prototype.findAdjacentTabIndexClosestToEdgeRTL_=function(e,t,n,r,o){var i=o-t.rootLeft-r-n,a=o-t.rootRight-n,l=i+a;return i>0||l>0?e+1:a<0||l<0?e-1:-1},t.prototype.getKeyFromEvent_=function(e){return f.has(e.key)?e.key:d.get(e.keyCode)},t.prototype.isActivationKey_=function(e){return e===s.SPACE_KEY||e===s.ENTER_KEY},t.prototype.indexIsInRange_=function(e){return e>=0&&e<this.adapter_.getTabListLength()},t.prototype.isRTL_=function(){return this.adapter_.isRTL()},t.prototype.scrollIntoView_=function(e){var t=this.adapter_.getScrollPosition(),n=this.adapter_.getOffsetWidth(),r=this.adapter_.getTabDimensionsAtIndex(e),o=this.findAdjacentTabIndexClosestToEdge_(e,r,t,n);if(this.indexIsInRange_(o)){var i=this.calculateScrollIncrement_(e,o,t,n);this.adapter_.incrementScroll(i)}},t.prototype.scrollIntoViewRTL_=function(e){var t=this.adapter_.getScrollPosition(),n=this.adapter_.getOffsetWidth(),r=this.adapter_.getTabDimensionsAtIndex(e),o=this.adapter_.getScrollContentWidth(),i=this.findAdjacentTabIndexClosestToEdgeRTL_(e,r,t,n,o);if(this.indexIsInRange_(i)){var a=this.calculateScrollIncrementRTL_(e,i,t,n,o);this.adapter_.incrementScroll(a)}},t}(u.a),m={ANIMATING:\"mdc-tab-scroller--animating\",SCROLL_AREA_SCROLL:\"mdc-tab-scroller__scroll-area--scroll\",SCROLL_TEST:\"mdc-tab-scroller__test\"},y={AREA_SELECTOR:\".mdc-tab-scroller__scroll-area\",CONTENT_SELECTOR:\".mdc-tab-scroller__scroll-content\"},v=function(){return function(e){this.adapter_=e}}(),g=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return l.b(t,e),t.prototype.getScrollPositionRTL=function(){var e=this.adapter_.getScrollAreaScrollLeft(),t=this.calculateScrollEdges_().right;return Math.round(t-e)},t.prototype.scrollToRTL=function(e){var t=this.calculateScrollEdges_(),n=this.adapter_.getScrollAreaScrollLeft(),r=this.clampScrollValue_(t.right-e);return{finalScrollPosition:r,scrollDelta:r-n}},t.prototype.incrementScrollRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft(),n=this.clampScrollValue_(t-e);return{finalScrollPosition:n,scrollDelta:n-t}},t.prototype.getAnimatingScrollPosition=function(e){return e},t.prototype.calculateScrollEdges_=function(){return{left:0,right:this.adapter_.getScrollContentOffsetWidth()-this.adapter_.getScrollAreaOffsetWidth()}},t.prototype.clampScrollValue_=function(e){var t=this.calculateScrollEdges_();return Math.min(Math.max(t.left,e),t.right)},t}(v),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return l.b(t,e),t.prototype.getScrollPositionRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft();return Math.round(e-t)},t.prototype.scrollToRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft(),n=this.clampScrollValue_(-e);return{finalScrollPosition:n,scrollDelta:n-t}},t.prototype.incrementScrollRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft(),n=this.clampScrollValue_(t-e);return{finalScrollPosition:n,scrollDelta:n-t}},t.prototype.getAnimatingScrollPosition=function(e,t){return e-t},t.prototype.calculateScrollEdges_=function(){var e=this.adapter_.getScrollContentOffsetWidth();return{left:this.adapter_.getScrollAreaOffsetWidth()-e,right:0}},t.prototype.clampScrollValue_=function(e){var t=this.calculateScrollEdges_();return Math.max(Math.min(t.right,e),t.left)},t}(v),_=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return l.b(t,e),t.prototype.getScrollPositionRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft();return Math.round(t-e)},t.prototype.scrollToRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft(),n=this.clampScrollValue_(e);return{finalScrollPosition:n,scrollDelta:t-n}},t.prototype.incrementScrollRTL=function(e){var t=this.adapter_.getScrollAreaScrollLeft(),n=this.clampScrollValue_(t+e);return{finalScrollPosition:n,scrollDelta:t-n}},t.prototype.getAnimatingScrollPosition=function(e,t){return e+t},t.prototype.calculateScrollEdges_=function(){return{left:this.adapter_.getScrollContentOffsetWidth()-this.adapter_.getScrollAreaOffsetWidth(),right:0}},t.prototype.clampScrollValue_=function(e){var t=this.calculateScrollEdges_();return Math.min(Math.max(t.right,e),t.left)},t}(v),E=function(e){function t(n){var r=e.call(this,l.a({},t.defaultAdapter,n))||this;return r.isAnimating_=!1,r}return l.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return m},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return y},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{eventTargetMatchesSelector:function(){return!1},addClass:function(){},removeClass:function(){},addScrollAreaClass:function(){},setScrollAreaStyleProperty:function(){},setScrollContentStyleProperty:function(){},getScrollContentStyleValue:function(){return\"\"},setScrollAreaScrollLeft:function(){},getScrollAreaScrollLeft:function(){return 0},getScrollContentOffsetWidth:function(){return 0},getScrollAreaOffsetWidth:function(){return 0},computeScrollAreaClientRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},computeScrollContentClientRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},computeHorizontalScrollbarHeight:function(){return 0}}},enumerable:!0,configurable:!0}),t.prototype.init=function(){var e=this.adapter_.computeHorizontalScrollbarHeight();this.adapter_.setScrollAreaStyleProperty(\"margin-bottom\",-e+\"px\"),this.adapter_.addScrollAreaClass(t.cssClasses.SCROLL_AREA_SCROLL)},t.prototype.getScrollPosition=function(){if(this.isRTL_())return this.computeCurrentScrollPositionRTL_();var e=this.calculateCurrentTranslateX_();return this.adapter_.getScrollAreaScrollLeft()-e},t.prototype.handleInteraction=function(){this.isAnimating_&&this.stopScrollAnimation_()},t.prototype.handleTransitionEnd=function(e){var n=e.target;this.isAnimating_&&this.adapter_.eventTargetMatchesSelector(n,t.strings.CONTENT_SELECTOR)&&(this.isAnimating_=!1,this.adapter_.removeClass(t.cssClasses.ANIMATING))},t.prototype.incrementScroll=function(e){if(0!==e)return this.isRTL_()?this.incrementScrollRTL_(e):void this.incrementScroll_(e)},t.prototype.scrollTo=function(e){if(this.isRTL_())return this.scrollToRTL_(e);this.scrollTo_(e)},t.prototype.getRTLScroller=function(){return this.rtlScrollerInstance_||(this.rtlScrollerInstance_=this.rtlScrollerFactory_()),this.rtlScrollerInstance_},t.prototype.calculateCurrentTranslateX_=function(){var e=this.adapter_.getScrollContentStyleValue(\"transform\");if(\"none\"===e)return 0;var t=/\\((.+?)\\)/.exec(e);if(!t)return 0;var n=t[1],r=l.c(n.split(\",\"),6),o=(r[0],r[1],r[2],r[3],r[4]);r[5];return parseFloat(o)},t.prototype.clampScrollValue_=function(e){var t=this.calculateScrollEdges_();return Math.min(Math.max(t.left,e),t.right)},t.prototype.computeCurrentScrollPositionRTL_=function(){var e=this.calculateCurrentTranslateX_();return this.getRTLScroller().getScrollPositionRTL(e)},t.prototype.calculateScrollEdges_=function(){return{left:0,right:this.adapter_.getScrollContentOffsetWidth()-this.adapter_.getScrollAreaOffsetWidth()}},t.prototype.scrollTo_=function(e){var t=this.getScrollPosition(),n=this.clampScrollValue_(e),r=n-t;this.animate_({finalScrollPosition:n,scrollDelta:r})},t.prototype.scrollToRTL_=function(e){var t=this.getRTLScroller().scrollToRTL(e);this.animate_(t)},t.prototype.incrementScroll_=function(e){var t=this.getScrollPosition(),n=e+t,r=this.clampScrollValue_(n),o=r-t;this.animate_({finalScrollPosition:r,scrollDelta:o})},t.prototype.incrementScrollRTL_=function(e){var t=this.getRTLScroller().incrementScrollRTL(e);this.animate_(t)},t.prototype.animate_=function(e){var n=this;0!==e.scrollDelta&&(this.stopScrollAnimation_(),this.adapter_.setScrollAreaScrollLeft(e.finalScrollPosition),this.adapter_.setScrollContentStyleProperty(\"transform\",\"translateX(\"+e.scrollDelta+\"px)\"),this.adapter_.computeScrollAreaClientRect(),requestAnimationFrame(function(){n.adapter_.addClass(t.cssClasses.ANIMATING),n.adapter_.setScrollContentStyleProperty(\"transform\",\"none\")}),this.isAnimating_=!0)},t.prototype.stopScrollAnimation_=function(){this.isAnimating_=!1;var e=this.getAnimatingScrollPosition_();this.adapter_.removeClass(t.cssClasses.ANIMATING),this.adapter_.setScrollContentStyleProperty(\"transform\",\"translateX(0px)\"),this.adapter_.setScrollAreaScrollLeft(e)},t.prototype.getAnimatingScrollPosition_=function(){var e=this.calculateCurrentTranslateX_(),t=this.adapter_.getScrollAreaScrollLeft();return this.isRTL_()?this.getRTLScroller().getAnimatingScrollPosition(t,e):t-e},t.prototype.rtlScrollerFactory_=function(){var e=this.adapter_.getScrollAreaScrollLeft();this.adapter_.setScrollAreaScrollLeft(e-1);var t=this.adapter_.getScrollAreaScrollLeft();if(t<0)return this.adapter_.setScrollAreaScrollLeft(e),new b(this.adapter_);var n=this.adapter_.computeScrollAreaClientRect(),r=this.adapter_.computeScrollContentClientRect(),o=Math.round(r.right-n.right);return this.adapter_.setScrollAreaScrollLeft(e),o===t?new _(this.adapter_):new g(this.adapter_)},t.prototype.isRTL_=function(){return\"rtl\"===this.adapter_.getScrollContentStyleValue(\"direction\")},t}(u.a);function T(e,t){if(void 0===t&&(t=!0),t&&\"undefined\"!==typeof p)return p;var n=e.createElement(\"div\");n.classList.add(m.SCROLL_TEST),e.body.appendChild(n);var r=n.offsetHeight-n.clientHeight;return e.body.removeChild(n),t&&(p=r),r}var C=n(98),O=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),w=function(){return(w=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},S=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},x=Object(i.a)({displayName:\"TabScroller\",classNames:[\"mdc-tab-scroller\"]}),I=Object(i.a)({displayName:\"TabScrollerScrollArea\",classNames:[\"mdc-tab-scroller__scroll-area\"]}),A=Object(i.a)({displayName:\"TabScrollerScrollContent\",classNames:[\"mdc-tab-scroller__scroll-content\"]}),k=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.area=n.createElement(\"area\"),n.content=n.createElement(\"content\"),n.handleInteraction=n.handleInteraction.bind(n),n.handleTransitionEnd=n.handleTransitionEnd.bind(n),n}return O(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new E({eventTargetMatchesSelector:function(e,t){return Object(C.b)(e,t)},addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},addScrollAreaClass:function(t){return e.area.addClass(t)},setScrollAreaStyleProperty:function(t,n){return e.area.setStyle(t,n)},setScrollContentStyleProperty:function(t,n){return e.content.setStyle(t,n)},getScrollContentStyleValue:function(t){return e.content.ref&&window.getComputedStyle(e.content.ref).getPropertyValue(t)||\"none\"},setScrollAreaScrollLeft:function(t){return e.area.ref&&(e.area.ref.scrollLeft=t)},getScrollAreaScrollLeft:function(){return e.area.ref?e.area.ref.scrollLeft:0},getScrollContentOffsetWidth:function(){return e.content.ref?e.content.ref.offsetWidth:0},getScrollAreaOffsetWidth:function(){return e.area.ref?e.area.ref.offsetWidth:0},computeScrollAreaClientRect:function(){return e.area.ref?e.area.ref.getBoundingClientRect():{}},computeScrollContentClientRect:function(){return e.content.ref?e.content.ref.getBoundingClientRect():{}},computeHorizontalScrollbarHeight:function(){return r.computeHorizontalScrollbarHeight(document)}})},t.prototype.getScrollPosition=function(){return this.foundation.getScrollPosition()},t.prototype.getScrollContentWidth=function(){return this.content.ref?this.content.ref.offsetWidth:0},t.prototype.incrementScroll=function(e){this.foundation.incrementScroll(e)},t.prototype.scrollTo=function(e){this.foundation.scrollTo(e)},t.prototype.handleInteraction=function(){this.foundation.handleInteraction()},t.prototype.handleTransitionEnd=function(e){this.foundation.handleTransitionEnd(e)},t.prototype.render=function(){var e=this.props,t=e.children,n=S(e,[\"children\"]);return o.createElement(x,w({},this.root.props(n),{ref:this.root.setRef}),o.createElement(I,w({},this.area.props({}),{ref:this.area.setRef,onWheel:this.handleInteraction,onTouchStart:this.handleInteraction,onPointerDown:this.handleInteraction,onMouseDown:this.handleInteraction,onKeyDown:this.handleInteraction}),o.createElement(A,w({},this.content.props({}),{ref:this.content.setRef,onTransitionEnd:this.handleTransitionEnd}),t)))},t}(a.a),P=n(25);n.d(t,\"a\",function(){return j});var N=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),L=function(){return(L=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},R=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},D=function(e,t){var n=\"function\"===typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(l){o={error:l}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a},M=Object(i.a)({displayName:\"TabBarRoot\",tag:\"nav\",classNames:function(e){return[\"mdc-tab-bar\",{\"mdc-tab-scroller__scroll-frame__tabs\":e.isTabScroller}]},consumeProps:[\"isTabScroller\"]}),j=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.currentActiveTabIndex=n.props.activeTabIndex||0,n.tabScroller=null,n.tabList=[],n.contextApi={onTabInteraction:function(e){return n.handleTabInteraction(e)},registerTab:function(e){return n.tabList.push(e)},unregisterTab:function(e){return n.tabList.splice(n.tabList.indexOf(e),1)}},n.handleKeyDown=n.handleKeyDown.bind(n),n.handleTabInteraction=n.handleTabInteraction.bind(n),n}return N(t,e),t.prototype.componentDidMount=function(){e.prototype.componentDidMount.call(this);var t=window.document.activeElement,n=D([window.scrollX,window.scrollY],2),r=n[0],o=n[1];this.foundation.adapter_.activateTabAtIndex(this.props.activeTabIndex||0,this.foundation.adapter_.getTabIndicatorClientRectAtIndex(void 0)),this.foundation.scrollIntoView(this.props.activeTabIndex||0),t&&t.focus(),window.scrollTo(r,o)},t.prototype.activateTab=function(e){var t=this,n=this.foundation;this.currentActiveTabIndex=e;var r=n.adapter_.getPreviousActiveTabIndex();n.indexIsInRange_(e)&&e!==r&&(n.adapter_.notifyTabActivated(e),setTimeout(function(){t.props.activeTabIndex===e||void 0===t.props.activeTabIndex?(n.adapter_.deactivateTabAtIndex(r),n.adapter_.activateTabAtIndex(e,n.adapter_.getTabIndicatorClientRectAtIndex(r)),n.scrollIntoView(e)):t.currentActiveTabIndex=r}))},t.prototype.getDefaultFoundation=function(){var e=this;return new h({scrollTo:function(t){e.tabScroller&&e.tabScroller.scrollTo(t)},incrementScroll:function(t){return e.tabScroller&&e.tabScroller.incrementScroll(t)},getScrollPosition:function(){return e.tabScroller?e.tabScroller.getScrollPosition():0},getScrollContentWidth:function(){return e.tabScroller?e.tabScroller.getScrollContentWidth():0},getOffsetWidth:function(){return e.root.ref?e.root.ref.offsetWidth:0},isRTL:function(){return!!e.root.ref&&\"rtl\"===window.getComputedStyle(e.root.ref).getPropertyValue(\"direction\")},setActiveTab:function(t){return e.activateTab(t)},activateTabAtIndex:function(t,n){e.tabList[t]&&e.tabList[t].activate(n)},deactivateTabAtIndex:function(t){return e.tabList[t]&&e.tabList[t].deactivate()},focusTabAtIndex:function(t){return e.tabList[t].focus()},getTabIndicatorClientRectAtIndex:function(t){return e.tabList[t]&&e.tabList[t].computeIndicatorClientRect()},getTabDimensionsAtIndex:function(t){return e.tabList[t]&&e.tabList[t].computeDimensions()},getPreviousActiveTabIndex:function(){for(var t=0;t<e.tabList.length;t++)if(e.tabList[t].active)return t;return-1},getFocusedTabIndex:function(){var t=e.getTabElements(),n=document.activeElement;return t?t.indexOf(n):-1},getIndexOfTabById:function(t){for(var n=0;n<e.tabList.length;n++)if(e.tabList[n].id===t)return n;return-1},getTabListLength:function(){return e.tabList.length},notifyTabActivated:function(t){return e.emit(\"onActivate\",{index:t},!0)}})},t.prototype.sync=function(e,t){e.activeTabIndex!==t.activeTabIndex&&e.activeTabIndex!==this.currentActiveTabIndex&&\"number\"===typeof e.activeTabIndex&&this.activateTab(e.activeTabIndex)},t.prototype.getTabElements=function(){return[].slice.call(this.root.ref&&this.root.ref.querySelectorAll(h.strings.TAB_SELECTOR))},t.prototype.handleTabInteraction=function(e){this.foundation.handleTabInteraction(e)},t.prototype.handleKeyDown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e),this.foundation.handleKeyDown(e)},t.prototype.render=function(){var e=this,t=this.props,n=t.children,r=(t.activeTabIndex,t.onActivate,R(t,[\"children\",\"activeTabIndex\",\"onActivate\"]));return o.createElement(P.a.Provider,{value:this.contextApi},o.createElement(M,L({},r,{ref:this.root.setRef,onKeyDown:this.handleKeyDown}),o.createElement(k,{ref:function(t){return e.tabScroller=t}},n)))},t.displayName=\"TabBar\",t}(a.a)},function(e,t,n){\"use strict\";var r=n(0),o=n(1),i=n(11),a={ACTIVE:\"mdc-tab--active\"},l={ARIA_SELECTED:\"aria-selected\",CONTENT_SELECTOR:\".mdc-tab__content\",INTERACTED_EVENT:\"MDCTab:interacted\",RIPPLE_SELECTOR:\".mdc-tab__ripple\",TABINDEX:\"tabIndex\",TAB_INDICATOR_SELECTOR:\".mdc-tab-indicator\"},u=function(e){function t(n){var r=e.call(this,o.a({},t.defaultAdapter,n))||this;return r.focusOnActivate_=!0,r}return o.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return a},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return l},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setAttr:function(){},activateIndicator:function(){},deactivateIndicator:function(){},notifyInteracted:function(){},getOffsetLeft:function(){return 0},getOffsetWidth:function(){return 0},getContentOffsetLeft:function(){return 0},getContentOffsetWidth:function(){return 0},focus:function(){}}},enumerable:!0,configurable:!0}),t.prototype.handleClick=function(){this.adapter_.notifyInteracted()},t.prototype.isActive=function(){return this.adapter_.hasClass(a.ACTIVE)},t.prototype.setFocusOnActivate=function(e){this.focusOnActivate_=e},t.prototype.activate=function(e){this.adapter_.addClass(a.ACTIVE),this.adapter_.setAttr(l.ARIA_SELECTED,\"true\"),this.adapter_.setAttr(l.TABINDEX,\"0\"),this.adapter_.activateIndicator(e),this.focusOnActivate_&&this.adapter_.focus()},t.prototype.deactivate=function(){this.isActive()&&(this.adapter_.removeClass(a.ACTIVE),this.adapter_.setAttr(l.ARIA_SELECTED,\"false\"),this.adapter_.setAttr(l.TABINDEX,\"-1\"),this.adapter_.deactivateIndicator())},t.prototype.computeDimensions=function(){var e=this.adapter_.getOffsetWidth(),t=this.adapter_.getOffsetLeft(),n=this.adapter_.getContentOffsetWidth(),r=this.adapter_.getContentOffsetLeft();return{contentLeft:t+r,contentRight:t+r+n,rootLeft:t,rootRight:t+e}},t}(i.a),s=n(6),c=n(100),f=n(36),d=n(5),p=n(7),h=n(25),m={ACTIVE:\"mdc-tab-indicator--active\",FADE:\"mdc-tab-indicator--fade\",NO_TRANSITION:\"mdc-tab-indicator--no-transition\"},y={CONTENT_SELECTOR:\".mdc-tab-indicator__content\"},v=function(e){function t(n){return e.call(this,o.a({},t.defaultAdapter,n))||this}return o.b(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return m},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"strings\",{get:function(){return y},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClass:function(){},removeClass:function(){},computeContentClientRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},setContentStyleProperty:function(){}}},enumerable:!0,configurable:!0}),t.prototype.computeContentClientRect=function(){return this.adapter_.computeContentClientRect()},t}(i.a),g=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o.b(t,e),t.prototype.activate=function(e){if(e){var t=this.computeContentClientRect(),n=e.width/t.width,r=e.left-t.left;this.adapter_.addClass(v.cssClasses.NO_TRANSITION),this.adapter_.setContentStyleProperty(\"transform\",\"translateX(\"+r+\"px) scaleX(\"+n+\")\"),this.computeContentClientRect(),this.adapter_.removeClass(v.cssClasses.NO_TRANSITION),this.adapter_.addClass(v.cssClasses.ACTIVE),this.adapter_.setContentStyleProperty(\"transform\",\"\")}else this.adapter_.addClass(v.cssClasses.ACTIVE)},t.prototype.deactivate=function(){this.adapter_.removeClass(v.cssClasses.ACTIVE)},t}(v),b=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),_=function(){return(_=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},E=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.root=t.createElement(\"root\"),t.content=t.createElement(\"content\"),t}return b(t,e),t.prototype.getDefaultFoundation=function(){var e=this;return new g({addClass:function(t){e.root.addClass(t)},removeClass:function(t){e.root.removeClass(t)},computeContentClientRect:function(){return e.content.ref?e.content.ref.getBoundingClientRect():{}},setContentStyleProperty:function(t,n){e.content.setStyle(t,n)}})},t.prototype.activate=function(e){this.foundation.activate(e)},t.prototype.deactivate=function(){this.foundation.deactivate()},t.prototype.computeContentClientRect=function(){return this.foundation.computeContentClientRect()},t.prototype.render=function(){return r.createElement(\"span\",_({},this.root.props({className:\"mdc-tab-indicator\"})),r.createElement(\"span\",_({ref:this.content.setRef},this.content.props({}),{className:\"mdc-tab-indicator__content mdc-tab-indicator__content--underline\"})))},t}(f.a);n.d(t,\"a\",function(){return I});var T,C=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),O=function(){return(O=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},w=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},S=Object(p.b)({surface:!1})(Object(s.a)({displayName:\"TabRoot\",tag:\"button\",classNames:function(e){return[\"mdc-tab\",{\"mdc-tab--stacked\":e.stacked}]},consumeProps:[\"stacked\"]})),x=Object(s.a)({displayName:\"TabIcon\",tag:d.a,classNames:[\"mdc-tab__icon\"]}),I=Object(h.b)()(((T=function(e){function t(t){var n=e.call(this,t)||this;return n._id=Object(c.a)(\"tab\"),n.root=n.createElement(\"root\"),n.tabIndicator=null,n.content=null,n.props.contextApi&&n.props.contextApi.registerTab(n),n.handleClick=n.handleClick.bind(n),n}return C(t,e),t.prototype.componentWillUnmount=function(){this.props.contextApi&&this.props.contextApi.unregisterTab(this)},Object.defineProperty(t.prototype,\"id\",{get:function(){return this.props.id?this.props.id:this._reactInternalFiber.key||this._id},enumerable:!0,configurable:!0}),t.prototype.getDefaultFoundation=function(){var e=this;return new u({setAttr:function(t,n){return e.root.setProp(t,n)},addClass:function(t){return e.root.addClass(t)},removeClass:function(t){return e.root.removeClass(t)},hasClass:function(t){return e.root.hasClass(t)},activateIndicator:function(t){return e.tabIndicator&&e.tabIndicator.activate(t)},deactivateIndicator:function(){return e.tabIndicator&&e.tabIndicator.deactivate()},notifyInteracted:function(){var t=e.emit(\"onInteraction\",{tabId:e.id},!0);e.props.contextApi&&e.props.contextApi.onTabInteraction(t)},getOffsetLeft:function(){return e.root.ref?e.root.ref.offsetLeft:0},getOffsetWidth:function(){return e.root.ref?e.root.ref.offsetWidth:0},getContentOffsetLeft:function(){return e.content?e.content.offsetLeft:0},getContentOffsetWidth:function(){return e.content?e.content.offsetWidth:0},focus:function(){return e.root.ref&&e.root.ref.focus&&e.root.ref.focus()}})},t.prototype.handleClick=function(e){this.props.onClick&&this.props.onClick(e),this.foundation.handleClick()},Object.defineProperty(t.prototype,\"active\",{get:function(){return this.foundation.isActive()},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,\"focusOnActivate\",{set:function(e){this.foundation.setFocusOnActivate(e)},enumerable:!0,configurable:!0}),t.prototype.activate=function(e){this.foundation.activate(e)},t.prototype.deactivate=function(){this.foundation.deactivate()},t.prototype.computeIndicatorClientRect=function(){return this.tabIndicator&&this.tabIndicator.computeContentClientRect()},t.prototype.computeDimensions=function(){return this.foundation.computeDimensions()},t.prototype.focus=function(){this.root.ref&&this.root.ref&&this.root.ref.focus()},t.prototype.render=function(){var e=this,t=this.props,n=t.children,o=t.label,i=t.icon,a=t.stacked,l=t.restrictIndicator,u=(t.onInteraction,t.contextApi,w(t,[\"children\",\"label\",\"icon\",\"stacked\",\"restrictIndicator\",\"onInteraction\",\"contextApi\"]));return r.createElement(S,O({},this.root.props(u),{onClick:this.handleClick,stacked:a,ref:this.root.setRef,ripple:{surface:!1}}),r.createElement(\"div\",{className:\"mdc-tab__content\",ref:function(t){return e.content=t}},!!i&&r.createElement(x,{icon:i}),(void 0!==n||void 0!==o)&&r.createElement(\"span\",{className:\"mdc-tab__text-label\"},o,n),!!l&&r.createElement(E,{ref:function(t){return e.tabIndicator=t}})),!l&&r.createElement(E,{ref:function(t){return e.tabIndicator=t}}),r.createElement(p.a,{className:\"mdc-tab__ripple\"}))},t}(f.a)).displayName=\"TabFoundation\",T));I.displayName=\"Tab\"},function(e,t,n){\"use strict\";var r=n(0),o=n(1),i=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,\"cssClasses\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"strings\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"numbers\",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,\"defaultAdapter\",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}(),a={LIST_ITEM_ACTIVATED_CLASS:\"mdc-list-item--activated\",LIST_ITEM_CLASS:\"mdc-list-item\",LIST_ITEM_DISABLED_CLASS:\"mdc-list-item--disabled\",LIST_ITEM_SELECTED_CLASS:\"mdc-list-item--selected\",ROOT:\"mdc-list\"},l={ACTION_EVENT:\"MDCList:action\",ARIA_CHECKED:\"aria-checked\",ARIA_CHECKED_CHECKBOX_SELECTOR:'[role=\"checkbox\"][aria-checked=\"true\"]',ARIA_CHECKED_RADIO_SELECTOR:'[role=\"radio\"][aria-checked=\"true\"]',ARIA_CURRENT:\"aria-current\",ARIA_ORIENTATION:\"aria-orientation\",ARIA_ORIENTATION_HORIZONTAL:\"horizontal\",ARIA_ROLE_CHECKBOX_SELECTOR:'[role=\"checkbox\"]',ARIA_SELECTED:\"aria-selected\",CHECKBOX_RADIO_SELECTOR:'input[type=\"checkbox\"]:not(:disabled), input[type=\"radio\"]:not(:disabled)',CHECKBOX_SELECTOR:'input[type=\"checkbox\"]:not(:disabled)',CHILD_ELEMENTS_TO_TOGGLE_TABINDEX:\"\\n    .\"+a.LIST_ITEM_CLASS+\" button:not(:disabled),\\n    .\"+a.LIST_ITEM_CLASS+\" a\\n  \",FOCUSABLE_CHILD_ELEMENTS:\"\\n    .\"+a.LIST_ITEM_CLASS+\" button:not(:disabled),\\n    .\"+a.LIST_ITEM_CLASS+\" a,\\n    .\"+a.LIST_ITEM_CLASS+' input[type=\"radio\"]:not(:disabled),\\n    .'+a.LIST_ITEM_CLASS+' input[type=\"checkbox\"]:not(:disabled)\\n  ',RADIO_SELECTOR:'input[type=\"radio\"]:not(:disabled)'},u={UNSET_INDEX:-1},s=[\"input\",\"button\",\"textarea\",\"select\"];var c=function(e){function t(n){var r=e.call(this,o.a({},t.defaultAdapter,n))||this;return r.wrapFocus_=!1,r.isVertical_=!0,r.isSingleSelectionList_=!1,r.selectedIndex_=u.UNSET_INDEX,r.focusedItemIndex_=u.UNSET_INDEX,r.useActivatedClass_=!1,r.ariaCurrentAttrValue_=null,r.isCheckboxList_=!1,r.isRadioList_=!1,r}return o.b(t,e),Object.defineProperty(t,\"strings\",{get:function(){return l},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"cssClasses\",{get:function(){return a},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"numbers\",{get:function(){return u},enumerable:!0,configurable:!0}),Object.defineProperty(t,\"defaultAdapter\",{get:function(){return{addClassForElementIndex:function(){},focusItemAtIndex:function(){},getAttributeForElementIndex:function(){return null},getFocusedElementIndex:function(){return 0},getListItemCount:function(){return 0},hasCheckboxAtIndex:function(){return!1},hasRadioAtIndex:function(){return!1},isCheckboxCheckedAtIndex:function(){return!1},isFocusInsideList:function(){return!1},isRootFocused:function(){return!1},notifyAction:function(){},removeClassForElementIndex:function(){},setAttributeForElementIndex:function(){},setCheckedCheckboxOrRadioAtIndex:function(){},setTabIndexForListItemChildren:function(){}}},enumerable:!0,configurable:!0}),t.prototype.layout=function(){0!==this.adapter_.getListItemCount()&&(this.adapter_.hasCheckboxAtIndex(0)?this.isCheckboxList_=!0:this.adapter_.hasRadioAtIndex(0)&&(this.isRadioList_=!0))},t.prototype.setWrapFocus=function(e){this.wrapFocus_=e},t.prototype.setVerticalOrientation=function(e){this.isVertical_=e},t.prototype.setSingleSelection=function(e){this.isSingleSelectionList_=e},t.prototype.setUseActivatedClass=function(e){this.useActivatedClass_=e},t.prototype.getSelectedIndex=function(){return this.selectedIndex_},t.prototype.setSelectedIndex=function(e){this.isIndexValid_(e)&&(this.isCheckboxList_?this.setCheckboxAtIndex_(e):this.isRadioList_?this.setRadioAtIndex_(e):this.setSingleSelectionAtIndex_(e))},t.prototype.handleFocusIn=function(e,t){t>=0&&this.adapter_.setTabIndexForListItemChildren(t,\"0\")},t.prototype.handleFocusOut=function(e,t){var n=this;t>=0&&this.adapter_.setTabIndexForListItemChildren(t,\"-1\"),setTimeout(function(){n.adapter_.isFocusInsideList()||n.setTabindexToFirstSelectedItem_()},0)},t.prototype.handleKeydown=function(e,t,n){var r=\"ArrowLeft\"===e.key||37===e.keyCode,o=\"ArrowUp\"===e.key||38===e.keyCode,i=\"ArrowRight\"===e.key||39===e.keyCode,a=\"ArrowDown\"===e.key||40===e.keyCode,l=\"Home\"===e.key||36===e.keyCode,u=\"End\"===e.key||35===e.keyCode,s=\"Enter\"===e.key||13===e.keyCode,c=\"Space\"===e.key||32===e.keyCode;if(this.adapter_.isRootFocused())o||u?(e.preventDefault(),this.focusLastElement()):(a||l)&&(e.preventDefault(),this.focusFirstElement());else{var f=this.adapter_.getFocusedElementIndex();if(!(-1===f&&(f=n)<0)){var d;if(this.isVertical_&&a||!this.isVertical_&&i)this.preventDefaultEvent_(e),d=this.focusNextElement(f);else if(this.isVertical_&&o||!this.isVertical_&&r)this.preventDefaultEvent_(e),d=this.focusPrevElement(f);else if(l)this.preventDefaultEvent_(e),d=this.focusFirstElement();else if(u)this.preventDefaultEvent_(e),d=this.focusLastElement();else if((s||c)&&t){var p=e.target;if(p&&\"A\"===p.tagName&&s)return;this.preventDefaultEvent_(e),this.isSelectableList_()&&this.setSelectedIndexOnAction_(f),this.adapter_.notifyAction(f)}this.focusedItemIndex_=f,void 0!==d&&(this.setTabindexAtIndex_(d),this.focusedItemIndex_=d)}}},t.prototype.handleClick=function(e,t){e!==u.UNSET_INDEX&&(this.isSelectableList_()&&this.setSelectedIndexOnAction_(e,t),this.adapter_.notifyAction(e),this.setTabindexAtIndex_(e),this.focusedItemIndex_=e)},t.prototype.focusNextElement=function(e){var t=e+1;if(t>=this.adapter_.getListItemCount()){if(!this.wrapFocus_)return e;t=0}return this.adapter_.focusItemAtIndex(t),t},t.prototype.focusPrevElement=function(e){var t=e-1;if(t<0){if(!this.wrapFocus_)return e;t=this.adapter_.getListItemCount()-1}return this.adapter_.focusItemAtIndex(t),t},t.prototype.focusFirstElement=function(){return this.adapter_.focusItemAtIndex(0),0},t.prototype.focusLastElement=function(){var e=this.adapter_.getListItemCount()-1;return this.adapter_.focusItemAtIndex(e),e},t.prototype.preventDefaultEvent_=function(e){var t=(\"\"+e.target.tagName).toLowerCase();-1===s.indexOf(t)&&e.preventDefault()},t.prototype.setSingleSelectionAtIndex_=function(e){if(this.selectedIndex_!==e){var t=a.LIST_ITEM_SELECTED_CLASS;this.useActivatedClass_&&(t=a.LIST_ITEM_ACTIVATED_CLASS),this.selectedIndex_!==u.UNSET_INDEX&&this.adapter_.removeClassForElementIndex(this.selectedIndex_,t),this.adapter_.addClassForElementIndex(e,t),this.setAriaForSingleSelectionAtIndex_(e),this.selectedIndex_=e}},t.prototype.setAriaForSingleSelectionAtIndex_=function(e){this.selectedIndex_===u.UNSET_INDEX&&(this.ariaCurrentAttrValue_=this.adapter_.getAttributeForElementIndex(e,l.ARIA_CURRENT));var t=null!==this.ariaCurrentAttrValue_,n=t?l.ARIA_CURRENT:l.ARIA_SELECTED;this.selectedIndex_!==u.UNSET_INDEX&&this.adapter_.setAttributeForElementIndex(this.selectedIndex_,n,\"false\");var r=t?this.ariaCurrentAttrValue_:\"true\";this.adapter_.setAttributeForElementIndex(e,n,r)},t.prototype.setRadioAtIndex_=function(e){this.adapter_.setCheckedCheckboxOrRadioAtIndex(e,!0),this.selectedIndex_!==u.UNSET_INDEX&&this.adapter_.setAttributeForElementIndex(this.selectedIndex_,l.ARIA_CHECKED,\"false\"),this.adapter_.setAttributeForElementIndex(e,l.ARIA_CHECKED,\"true\"),this.selectedIndex_=e},t.prototype.setCheckboxAtIndex_=function(e){for(var t=0;t<this.adapter_.getListItemCount();t++){var n=!1;e.indexOf(t)>=0&&(n=!0),this.adapter_.setCheckedCheckboxOrRadioAtIndex(t,n),this.adapter_.setAttributeForElementIndex(t,l.ARIA_CHECKED,n?\"true\":\"false\")}this.selectedIndex_=e},t.prototype.setTabindexAtIndex_=function(e){this.focusedItemIndex_===u.UNSET_INDEX&&0!==e?this.adapter_.setAttributeForElementIndex(0,\"tabindex\",\"-1\"):this.focusedItemIndex_>=0&&this.focusedItemIndex_!==e&&this.adapter_.setAttributeForElementIndex(this.focusedItemIndex_,\"tabindex\",\"-1\"),this.adapter_.setAttributeForElementIndex(e,\"tabindex\",\"0\")},t.prototype.isSelectableList_=function(){return this.isSingleSelectionList_||this.isCheckboxList_||this.isRadioList_},t.prototype.setTabindexToFirstSelectedItem_=function(){var e=0;this.isSelectableList_()&&(\"number\"===typeof this.selectedIndex_&&this.selectedIndex_!==u.UNSET_INDEX?e=this.selectedIndex_:this.selectedIndex_ instanceof Array&&this.selectedIndex_.length>0&&(e=this.selectedIndex_.reduce(function(e,t){return Math.min(e,t)}))),this.setTabindexAtIndex_(e)},t.prototype.isIndexValid_=function(e){var t=this;if(e instanceof Array){if(!this.isCheckboxList_)throw new Error(\"MDCListFoundation: Array of index is only supported for checkbox based list\");return 0===e.length||e.some(function(e){return t.isIndexInRange_(e)})}if(\"number\"===typeof e){if(this.isCheckboxList_)throw new Error(\"MDCListFoundation: Expected array of index for checkbox based list but got number: \"+e);return this.isIndexInRange_(e)}return!1},t.prototype.isIndexInRange_=function(e){var t=this.adapter_.getListItemCount();return e>=0&&e<t},t.prototype.setSelectedIndexOnAction_=function(e,t){void 0===t&&(t=!0),this.isCheckboxList_?this.toggleCheckboxAtIndex_(e,t):this.setSelectedIndex(e)},t.prototype.toggleCheckboxAtIndex_=function(e,t){var n=this.adapter_.isCheckboxCheckedAtIndex(e);t&&(n=!n,this.adapter_.setCheckedCheckboxOrRadioAtIndex(e,n)),this.adapter_.setAttributeForElementIndex(e,l.ARIA_CHECKED,n?\"true\":\"false\");var r=this.selectedIndex_===u.UNSET_INDEX?[]:this.selectedIndex_.slice();n?r.push(e):r=r.filter(function(t){return t!==e}),this.selectedIndex_=r},t}(i),f=n(6),d=n(98),p=n(36);n.d(t,\"a\",function(){return g});var h=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(t,n)};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),m=function(){return(m=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},y=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&\"function\"===typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&(n[r[o]]=e[r[o]])}return n},v=Object(f.a)({displayName:\"ListRoot\",defaultProps:{dense:void 0,twoLine:void 0,avatarList:void 0,nonInteractive:void 0},classNames:function(e){return[\"mdc-list\",{\"mdc-list--dense\":e.dense,\"mdc-list--two-line\":e.twoLine,\"mdc-list--avatar-list\":e.avatarList,\"mdc-list--non-interactive\":e.nonInteractive}]},consumeProps:[\"dense\",\"twoLine\",\"avatarList\",\"nonInteractive\",\"onAction\"]}),g=function(e){function t(t){var n=e.call(this,t)||this;return n.root=n.createElement(\"root\"),n.handleClick=n.handleClick.bind(n),n.handleKeydown=n.handleKeydown.bind(n),n.handleFocusIn=n.handleFocusIn.bind(n),n.handleFocusOut=n.handleFocusOut.bind(n),n}return h(t,e),Object.defineProperty(t,\"cssClasses\",{get:function(){return c.cssClasses},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,\"listElements\",{get:function(){return this.root.ref?[].slice.call(this.root.ref.querySelectorAll(\".\"+c.cssClasses.LIST_ITEM_CLASS)):[]},enumerable:!0,configurable:!0}),t.prototype.componentDidMount=function(){e.prototype.componentDidMount.call(this),this.foundation.layout()},t.prototype.focusItemAtIndex=function(e){this.foundation.adapter_.focusItemAtIndex(e)},t.prototype.getDefaultFoundation=function(){var e=this;return new c(Object.assign({getListItemCount:function(){return e.listElements.length},getFocusedElementIndex:function(){return e.listElements.indexOf(document.activeElement)},setAttributeForElementIndex:function(t,n,r){if(\"tabindex\"!==n||-1!==r){var o=e.listElements[t];o&&o.setAttribute(n,String(r))}},removeAttributeForElementIndex:function(t,n){var r=e.listElements[t];r&&r.removeAttribute(n)},addClassForElementIndex:function(t,n){var r=e.listElements[t];r&&r.classList.add(n)},removeClassForElementIndex:function(t,n){var r=e.listElements[t];r&&r.classList.remove(n)},focusItemAtIndex:function(t){var n=e.listElements[t];n&&n.focus()},setTabIndexForListItemChildren:function(t,n){var r=e.listElements[t];[].slice.call(r.querySelectorAll(c.strings.CHILD_ELEMENTS_TO_TOGGLE_TABINDEX)).forEach(function(e){return e.setAttribute(\"tabindex\",String(n))})},hasCheckboxAtIndex:function(t){return!!e.listElements[t].querySelector(c.strings.CHECKBOX_SELECTOR)},hasRadioAtIndex:function(t){return!!e.listElements[t].querySelector(c.strings.RADIO_SELECTOR)},isCheckboxCheckedAtIndex:function(t){var n=e.listElements[t].querySelector(c.strings.CHECKBOX_SELECTOR);return!!n&&n.checked},setCheckedCheckboxOrRadioAtIndex:function(t,n){var r=e.listElements[t].querySelector(c.strings.CHECKBOX_RADIO_SELECTOR);if(r){r.checked=n;var o=document.createEvent(\"Event\");o.initEvent(\"change\",!0,!0),r.dispatchEvent(o)}},notifyAction:function(t){e.emit(\"onAction\",t)},isFocusInsideList:function(){return e.root.ref&&e.root.ref.contains(document.activeElement)}}))},t.prototype.getListItemIndex=function(e){for(var t=e.target,n=-1;t&&!t.classList.contains(c.cssClasses.LIST_ITEM_CLASS)&&!t.classList.contains(c.cssClasses.ROOT);)t=t.parentElement;return t&&t.classList.contains(c.cssClasses.LIST_ITEM_CLASS)&&(n=this.listElements.indexOf(t)),n},t.prototype.handleClick=function(e){this.props.onClick&&this.props.onClick(e);var t=this.getListItemIndex(e),n=!Object(d.b)(e.target,c.strings.CHECKBOX_RADIO_SELECTOR);this.foundation.handleClick(t,n)},t.prototype.handleKeydown=function(e){this.props.onKeyDown&&this.props.onKeyDown(e);var t=this.getListItemIndex(e);t>=0&&this.foundation.handleKeydown(e,e.target instanceof Element&&e.target.classList.contains(c.cssClasses.LIST_ITEM_CLASS),t)},t.prototype.handleFocusIn=function(e){this.props.onFocus&&this.props.onFocus(e),this.foundation.handleFocusIn(e,this.getListItemIndex(e))},t.prototype.handleFocusOut=function(e){this.props.onBlur&&this.props.onBlur(e),this.foundation.handleFocusOut(e,this.getListItemIndex(e))},t.prototype.render=function(){var e=y(this.props,[]);return r.createElement(v,m({},e,{ref:this.root.setRef,onClick:this.handleClick,onKeyDown:this.handleKeydown,onFocus:this.handleFocusIn,onBlur:this.handleFocusOut}))},t}(p.a)}]]);\n//# sourceMappingURL=2.7d84b3fa.chunk.js.map"
  },
  {
    "path": "ui/web/static/js/main.84d3ab8c.chunk.js",
    "content": "(this[\"webpackJsonpcayley-ui\"]=this[\"webpackJsonpcayley-ui\"]||[]).push([[0],{39:function(e,t,n){e.exports=n.p+\"static/media/logo.ca711468.svg\"},47:function(e,t,n){e.exports=n(84)},52:function(e,t,n){},80:function(e,t,n){},81:function(e,t,n){},83:function(e,t,n){},84:function(e,t,n){\"use strict\";n.r(t);var a=n(0),r=n.n(a),c=n(20),l=n.n(c),u=(n(52),n(4)),o=n(21),i=n(15),s=n(40),f=(n(53),n(101)),m=n(102),d=(n(54),n(55),n(56),n(57),n(9)),p=n.n(d),v=n(12),b=n(10),O=n.n(b),g=[{label:\"Gizmo\",value:\"gizmo\"},{label:\"GraphQL\",value:\"graphql\"},{label:\"MQL\",value:\"mql\"}];function y(e,t,n){return h.apply(this,arguments)}function h(){return(h=Object(v.a)(p.a.mark(function e(t,n,a){var r;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(\"\".concat(t,\"/api/v2/query?\").concat(new URLSearchParams({lang:n})),{method:\"POST\",headers:{Accept:\"application/ld+json\"},body:a});case 2:return r=e.sent,e.abrupt(\"return\",r.json());case 4:case\"end\":return e.stop()}},e)}))).apply(this,arguments)}var E=n(24),j=(n(32),n(26)),w=(n(33),n(14)),k=(n(34),function(e){var t=e.onClick;return r.a.createElement(w.a,{icon:\"play_circle_filled\",unelevated:!0,label:\"Run\",onClick:t})}),x=function(){var e=a.useState(),t=Object(u.a)(e,2),n=t[0],r=t[1];return[a.useCallback(function(e,t){r(t)},[r]),n]},C=g[0].value,S=\"LAST_QUERY\";var P={text:\"g.V().all()\",language:C},R=function(){var e=localStorage.getItem(S);return e?JSON.parse(e):null}()||P;function L(){return R}function q(e){R=e,function(e){localStorage.setItem(S,JSON.stringify(e))}(e)}var D=function(e){switch(e){case\"gizmo\":return\"javascript\";case\"graphql\":return\"graphql\";case\"mql\":return\"json\";default:throw new Error(\"Unexpected value \".concat(e))}},N=function(){var e=Object(v.a)(p.a.mark(function e(){var t,n,a;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return t=\"\".concat(\"\",\"/gizmo.d.ts\"),e.next=3,fetch(t);case 3:return n=e.sent,e.next=6,n.text();case 6:return a=e.sent,e.abrupt(\"return\",[a,t]);case 8:case\"end\":return e.stop()}},e)}));return function(){return e.apply(this,arguments)}}();function U(){return(U=Object(v.a)(p.a.mark(function e(t,n){var a;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t){e.next=2;break}return e.abrupt(\"return\");case 2:return e.next=4,b.monaco.init();case 4:a=e.sent,t.addAction({id:\"cayley-run\",label:\"Run\",keybindings:[a.KeyMod.CtrlCmd|a.KeyCode.Enter],run:function(){n()}});case 6:case\"end\":return e.stop()}},e)}))).apply(this,arguments)}var Q={minimap:{enabled:!1}};function T(){return(T=Object(v.a)(p.a.mark(function e(){var t,n,a,r,c,l,o,i;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,N();case 2:return t=e.sent,n=Object(u.a)(t,2),a=n[0],r=n[1],e.next=8,b.monaco.init();case 8:c=e.sent,l=c.languages.typescript,o=l.javascriptDefaults,i=l.ScriptTarget,o.setCompilerOptions({noLib:!0,target:i.ES5,allowNonTsExtensions:!0}),o.addExtraLib(a,r);case 12:case\"end\":return e.stop()}},e)}))).apply(this,arguments)}!function(){T.apply(this,arguments)}();var z=function(e){var t=e.onRun,n=L(),c=x(),l=Object(u.a)(c,2),o=l[0],i=l[1],s=Object(a.useState)(n.language),f=Object(u.a)(s,2),m=f[0],d=f[1];Object(a.useEffect)(function(){var e=L();d(e.language)},[d]);var p=Object(a.useCallback)(function(e,t){var n=L();t.setValue(n.text),o(e,t)},[o]),v=r.a.useCallback(function(e){d(e.target.value)},[d]),b=Object(a.useCallback)(function(){i&&t(i.getValue(),m)},[i,m,t]);return Object(a.useEffect)(function(){i&&function(e,t){U.apply(this,arguments)}(i,b)},[i,b]),Object(a.useEffect)(function(){return i&&i.onDidChangeModelContent(function(){q({text:i.getValue(),language:m})}),function(){i&&q({text:i.getValue(),language:m})}},[i,m]),r.a.createElement(\"div\",{className:\"QueryEditor\"},r.a.createElement(E.a,{use:\"headline6\"},\"Query Editor\"),r.a.createElement(O.a,{height:300,editorDidMount:p,language:D(m),options:Q}),r.a.createElement(\"div\",{className:\"actions\"},r.a.createElement(k,{onClick:b}),r.a.createElement(j.a,{outlined:!0,options:g,value:m,onChange:v})))},V={readOnly:!0,minimap:{enabled:!1},scrollBeyondLastLine:!1},A=function(e){var t=e.value;return r.a.createElement(O.a,{height:300,value:t?JSON.stringify(t,null,4):\"\",language:\"json\",options:V})},W=n(103),J=n(99),M=(n(35),n(5)),_=(n(79),n(80),function(e){var t=e.queries;return r.a.createElement(W.a,{className:\"QueryHistory\"},Object(i.a)(t).reverse().map(function(e){var t=g.find(function(t){return t.value===e.language});return r.a.createElement(J.d,{key:e.id},r.a.createElement(\"div\",{className:\"time\"},e.time.toLocaleString()),r.a.createElement(\"div\",{className:\"status\"},e.result?\"error\"in e.result?r.a.createElement(M.a,{icon:\"error\"}):r.a.createElement(M.a,{icon:\"check_circle\"}):null),r.a.createElement(\"div\",{className:\"language\"},t&&t.label),r.a.createElement(\"div\",{className:\"query\"},e.text))}))});function I(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,a)}return n}var B=null,F=[];var G=function(e){var t=e.serverURL,n=Object(a.useState)(0),c=Object(u.a)(n,2),l=c[0],d=c[1],p=Object(a.useState)(B),v=Object(u.a)(p,2),b=v[0],O=v[1],g=Object(a.useState)(F),h=Object(u.a)(g,2),E=h[0],j=h[1],w=r.a.useCallback(function(e,n){var a=E.length;O(a),j(function(t){return[].concat(Object(i.a)(t),[{id:a,text:e,result:null,language:n,time:new Date}])}),y(t,n,e).then(function(e){j(function(t){return t.map(function(t){return t.id===a?function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?I(n,!0).forEach(function(t){Object(o.a)(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):I(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}({},t,{result:e}):t})})}).catch(function(e){alert(e)})},[E,t]),k=E.find(function(e){return e.id===b}),x=k?k.result:null;return r.a.createElement(\"main\",null,r.a.createElement(z,{onRun:w}),r.a.createElement(s.a,null,r.a.createElement(f.a,{style:{width:\"30em\"},activeTabIndex:l,onActivate:function(e){return d(e.detail.index)}},r.a.createElement(m.a,null,\"Results\"),r.a.createElement(m.a,null,\"Query History\")),0===l&&r.a.createElement(A,{value:x}),1===l&&r.a.createElement(_,{queries:E})))},H=n(41);function K(){return(K=Object(v.a)(p.a.mark(function e(t,n,a){var r,c,l,u;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(\"\".concat(t,\"/api/v1/shape/\").concat(n),{method:\"POST\",body:a});case 2:return r=e.sent,e.next=5,r.json();case 5:if(c=e.sent,l=c.error,u=Object(H.a)(c,[\"error\"]),!l){e.next=10;break}throw new Error(l);case 10:return e.abrupt(\"return\",u);case 11:case\"end\":return e.stop()}},e)}))).apply(this,arguments)}var Y=function(e){var t=e.serverURL,n=Object(a.useState)(null),c=Object(u.a)(n,2),l=c[0],o=c[1],i=Object(a.useCallback)(function(e,n){(function(e,t,n){return K.apply(this,arguments)})(t,n,e).then(o)},[t]);return r.a.createElement(\"main\",null,r.a.createElement(z,{onRun:i}),r.a.createElement(A,{value:l}))},$=n(42),X=n(38),Z=n(17);function ee(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,a)}return n}var te={nodes:[],links:[]},ne=function(e){var t=e.data,n=e.linkComponent,c=e.nodeComponent,l=Object(a.useState)(te),i=Object(u.a)(l,2),s=i[0],f=i[1],m=Object(X.a)(),d=Object(u.a)(m,2),p=d[0],v=d[1],b=v.width,O=v.height;return Object(a.useEffect)(function(){t&&b&&O&&Object(Z.d)(t.nodes).force(\"link\",Object(Z.b)().id(function(e){return e.id}).links(t.links).distance(100)).force(\"charge\",Object(Z.c)()).force(\"center\",Object(Z.a)(b/2,O/2)).on(\"tick\",function(){f(function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?ee(n,!0).forEach(function(t){Object(o.a)(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ee(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}({},t))})},[t,O,b]),r.a.createElement(\"div\",{className:\"graph\",ref:p},r.a.createElement(\"svg\",null,r.a.createElement($.a,{graph:s,linkComponent:n,nodeComponent:c})))},ae=(n(81),function(e){var t=[],n=new Set,a=!0,r=!1,c=void 0;try{for(var l,u=e[Symbol.iterator]();!(a=(l=u.next()).done);a=!0){var o=l.value;if(\"source\"in o&&\"target\"in o){var s=o.source[\"@id\"],f=o.target[\"@id\"];n.add(s),n.add(f),t.push({source:s,target:f})}}}catch(m){r=!0,c=m}finally{try{a||null==u.return||u.return()}finally{if(r)throw c}}return{links:t,nodes:Object(i.a)(n.values()).map(function(e){return{id:e}})}}),re=function(e){var t=e.link;return r.a.createElement(\"line\",{x1:t.source.x,y1:t.source.y,x2:t.target.x,y2:t.target.y,strokeWidth:2,stroke:\"#999\",strokeOpacity:.6},r.a.createElement(\"text\",null,t.id))},ce=function(e){var t=e.node;return r.a.createElement(\"g\",null,r.a.createElement(\"circle\",{r:5,fill:\"#21D4FD\"}),r.a.createElement(\"text\",{textAnchor:\"end\"},t.id))},le=function(e){var t=e.serverURL,n=Object(a.useState)(null),c=Object(u.a)(n,2),l=c[0],o=c[1],i=Object(a.useCallback)(function(e,n){y(t,n,e).then(function(e){o(e)})},[t]);return r.a.createElement(\"main\",null,r.a.createElement(z,{onRun:i}),l&&\"result\"in l?r.a.createElement(ne,{data:ae(l.result),nodeComponent:ce,linkComponent:re}):null)},ue=function(e,t){return fetch(\"\".concat(e,\"/api/v2/write\"),{method:\"POST\",body:t})},oe=[{label:\"Write\",value:\"write\"},{label:\"Delete\",value:\"delete\"}],ie=function(e){var t=e.serverURL,n=Object(a.useState)(oe[0].value),c=Object(u.a)(n,2),l=c[0],o=c[1],i=x(),s=Object(u.a)(i,2),f=s[0],m=s[1],d=Object(a.useCallback)(function(){if(m){var e=m.getValue();switch(l){case\"write\":return void ue(t,e);case\"delete\":return void function(e,t){fetch(\"\".concat(e,\"/api/v2/delete\"),{method:\"POST\",body:t})}(t,e);default:throw new Error(\"Unexpected mode \".concat(l))}}},[t,m,l]),p=Object(a.useCallback)(function(e){o(e.target.value)},[o]),v=Object(a.useRef)(null),b=Object(a.useCallback)(function(){var e=v.current;e&&e.click()},[v]),g=Object(a.useCallback)(function(e){var n=!0,a=!1,r=void 0;try{for(var c,l=function(){var e=c.value;ue(t,e).then(function(){console.log(\"Uploaded \".concat(e.name))})},u=e.currentTarget.files[Symbol.iterator]();!(n=(c=u.next()).done);n=!0)l()}catch(o){a=!0,r=o}finally{try{n||null==u.return||u.return()}finally{if(a)throw r}}},[t]);return r.a.createElement(r.a.Fragment,null,r.a.createElement(\"input\",{type:\"file\",ref:v,style:{display:\"none\"},onChange:g}),r.a.createElement(\"main\",null,r.a.createElement(E.a,{use:\"headline6\"},\"Write\"),r.a.createElement(O.a,{editorDidMount:f,language:\"nquads\",options:{minimap:{enabled:!1}}}),r.a.createElement(\"div\",{className:\"actions\"},r.a.createElement(k,{onClick:d}),r.a.createElement(j.a,{outlined:!0,options:oe,value:l,onChange:p}),r.a.createElement(w.a,{label:\"Upload file\",onClick:b}))))},se=n(18),fe=(n(82),n(39)),me=n.n(fe),de=(n(83),\"http://localhost:64210\");var pe=function(){if(void 0===de)throw new Error(\"SERVER_URL environment variable must be provided\");var e=Object(a.useState)(\"query\"),t=Object(u.a)(e,2),n=t[0],c=t[1];return r.a.createElement(\"div\",{className:\"App\"},r.a.createElement(se.a,null,r.a.createElement(se.c,null,r.a.createElement(se.d,null,r.a.createElement(\"img\",{className:\"Logo\",src:me.a,alt:\"logo\"}),\"Cayley\")),r.a.createElement(se.b,null,r.a.createElement(W.a,null,r.a.createElement(J.d,{onClick:function(){return c(\"query\")},activated:\"query\"===n},\"Query\"),r.a.createElement(J.d,{activated:\"queryShape\"===n,onClick:function(){return c(\"queryShape\")}},\"Query Shape\"),r.a.createElement(J.d,{activated:\"visualize\"===n,onClick:function(){return c(\"visualize\")}},\"Visualize\"),r.a.createElement(J.d,{activated:\"write\"===n,onClick:function(){return c(\"write\")}},\"Write\")))),\"query\"===n&&r.a.createElement(G,{serverURL:de}),\"queryShape\"===n&&r.a.createElement(Y,{serverURL:de}),\"visualize\"===n&&r.a.createElement(le,{serverURL:de}),\"write\"===n&&r.a.createElement(ie,{serverURL:de}))};Boolean(\"localhost\"===window.location.hostname||\"[::1]\"===window.location.hostname||window.location.hostname.match(/^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));l.a.render(r.a.createElement(pe,null),document.getElementById(\"root\")),\"serviceWorker\"in navigator&&navigator.serviceWorker.ready.then(function(e){e.unregister()})}},[[47,1,2]]]);\n//# sourceMappingURL=main.84d3ab8c.chunk.js.map"
  },
  {
    "path": "ui/web/static/js/runtime-main.0686c6e7.js",
    "content": "!function(e){function r(r){for(var n,l,i=r[0],a=r[1],f=r[2],p=0,s=[];p<i.length;p++)l=i[p],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){\"undefined\"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&\"object\"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,\"default\",{enumerable:!0,value:e}),2&r&&\"string\"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,\"a\",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p=\"/\";var i=this[\"webpackJsonpcayley-ui\"]=this[\"webpackJsonpcayley-ui\"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var c=a;t()}([]);\n//# sourceMappingURL=runtime-main.0686c6e7.js.map"
  },
  {
    "path": "version/version.go",
    "content": "package version\n\n// Populated by goreleaser.\nvar (\n\tVersion   = \"v0.8.x-dev\"\n\tGitHash   = \"dev snapshot\"\n\tBuildDate string\n)\n"
  },
  {
    "path": "vet.sh",
    "content": "#!/bin/bash\n\n# Vet all the files using go vet excluding errors which are currently explicitly ignore\n# This script is intended to be used in the continuous integration process\n# When editing, it is highly recommended to use ShellCheck (https://www.shellcheck.net/) to avoid common pitfalls\n\n# Patterns to be ignored from the go lint output\nIGNORED_PATTERNS=(\n    \"^# \"\n\n    # Field order is well-defined\n    \"/quad\\.Quad composite literal uses unkeyed fields\" # 1.19\n    \"/quad\\.Quad struct literal uses unkeyed fields\"    # 1.20\n\n    # Code imported from b\n    \" method Seek\\(k int64\\) .* should have signature \"\n)\n\n# Patterns joined into a regular expression\nREGEX=$(printf \"|(%s)\" \"${IGNORED_PATTERNS[@]}\")\nREGEX=${REGEX:1}\n\n# Execute go vet on all the files and filter output by the regualr expression\noutput=$( (go vet ./... 2>&1 | grep -Ev \"$REGEX\") | tee /dev/fd/2);\nif [ -z \"$output\" ]\nthen\n    exit 0\nelse\n    exit 1\nfi\n"
  },
  {
    "path": "writer/single.go",
    "content": "// Copyright 2014 The Cayley Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage writer\n\nimport (\n\t\"github.com/cayleygraph/cayley/graph\"\n\t\"github.com/cayleygraph/quad\"\n)\n\nfunc init() {\n\tgraph.RegisterWriter(\"single\", NewSingleReplication)\n}\n\ntype Single struct {\n\tqs         graph.QuadStore\n\tignoreOpts graph.IgnoreOpts\n}\n\nfunc NewSingle(qs graph.QuadStore, opts graph.IgnoreOpts) (graph.QuadWriter, error) {\n\treturn &Single{\n\t\tqs:         qs,\n\t\tignoreOpts: opts,\n\t}, nil\n}\n\nfunc NewSingleReplication(qs graph.QuadStore, opts graph.Options) (graph.QuadWriter, error) {\n\tignoreMissing, err := opts.BoolKey(\"ignore_missing\", graph.IgnoreMissing)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tignoreDuplicate, err := opts.BoolKey(\"ignore_duplicate\", graph.IgnoreDuplicates)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewSingle(qs, graph.IgnoreOpts{\n\t\tIgnoreMissing: ignoreMissing,\n\t\tIgnoreDup:     ignoreDuplicate,\n\t})\n}\n\nfunc (s *Single) AddQuad(q quad.Quad) error {\n\tdeltas := make([]graph.Delta, 1)\n\tdeltas[0] = graph.Delta{\n\t\tQuad:   q,\n\t\tAction: graph.Add,\n\t}\n\treturn s.qs.ApplyDeltas(deltas, s.ignoreOpts)\n}\n\nfunc (s *Single) AddQuadSet(set []quad.Quad) error {\n\ttx := graph.NewTransactionN(len(set))\n\tfor _, q := range set {\n\t\ttx.AddQuad(q)\n\t}\n\treturn s.qs.ApplyDeltas(tx.Deltas, s.ignoreOpts)\n}\n\nfunc (s *Single) RemoveQuad(q quad.Quad) error {\n\tdeltas := make([]graph.Delta, 1)\n\tdeltas[0] = graph.Delta{\n\t\tQuad:   q,\n\t\tAction: graph.Delete,\n\t}\n\treturn s.qs.ApplyDeltas(deltas, s.ignoreOpts)\n}\n\n// RemoveNode removes all quads with the given value.\n//\n// It returns ErrNodeNotExists if node is missing.\nfunc (s *Single) RemoveNode(v quad.Value) error {\n\tgv, err := s.qs.ValueOf(v)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif gv == nil {\n\t\treturn graph.ErrNodeNotExists\n\t}\n\tdel := graph.NewRemover(s)\n\tdefer del.Close()\n\n\ttotal := 0\n\t// TODO(dennwc): QuadStore may remove node without iterations. Consider optional interface for this.\n\tfor _, d := range []quad.Direction{quad.Subject, quad.Predicate, quad.Object, quad.Label} {\n\t\tr := graph.NewResultReader(s.qs, s.qs.QuadIterator(d, gv).Iterate())\n\t\tn, err := quad.Copy(del, r)\n\t\tr.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttotal += n\n\t}\n\tif err := del.Flush(); err != nil {\n\t\treturn err\n\t}\n\tif total == 0 {\n\t\treturn graph.ErrNodeNotExists\n\t}\n\treturn nil\n}\n\nfunc (s *Single) Close() error {\n\t// Nothing to clean up locally.\n\treturn nil\n}\n\nfunc (s *Single) ApplyTransaction(t *graph.Transaction) error {\n\treturn s.qs.ApplyDeltas(t.Deltas, s.ignoreOpts)\n}\n"
  }
]