首页 > 解决方案 > 如何使用“graphql-compose-elasticsearch”重构 GraphQL 模式以将 GraphQL 查询转换为 Elasticsearch 查询?

问题描述

我在localhost:3000(REST api)上使用 json 服务器托管我的数据并使用 GraphQL 来获取数据,现在我想将数据移动到 Elastic Search 服务器中,我仍然想使用 GraphQL 作为 API 网关。我尝试使用这个graphql-compose-elasticsearch库来使用 GraphQL 作为 ElasticSearch 的代理。 https://github.com/graphql-compose/graphql-compose-elasticsearch

在我最初的 GraphQL 模式中,我定义了类型、根查询、解析器。代码是这样的:

const graphql = require('graphql');
const axios = require('axios');

const {
  GraphQLObjectType,
  GraphQLList,
  GraphQLID,
  GraphQLInt,
  GraphQLString,
  GraphQLBoolean,
  GraphQLSchema,
  GraphQLNonNull
} = graphql;

const RoomType = new GraphQLObjectType({
  name: 'RoomType',
  fields: () => ({
    id: { type: GraphQLID },
    roomName: { type: GraphQLString },
    roomNumber: { type: GraphQLString },
    floorId: { type: GraphQLInt },
    hasImages: { type: GraphQLBoolean },
    hasGLTF: { type: GraphQLBoolean },
    hasPhotogrammetry: { type: GraphQLBoolean },
    hasPointClouds: { type: GraphQLBoolean },
    roomDescription: { type: GraphQLString },
    floor: {
      type: FloorType,
      resolve(parentValue, args) {
        return axios
          .get(`http://localhost:3000/floors/${parentValue.floorId}`)
          .then((resp) => resp.data);
      }
    },
    assets: {
      type: new GraphQLList(AssetType),
      resolve(parentValue, args) {
        return axios
          .get(`http://localhost:3000/rooms/${parentValue.id}/assets`)
          .then((resp) => resp.data);
      }
    }
  })
});

const FloorType = new GraphQLObjectType({
  name: 'FloorType',
  fields: () => ({
    id: { type: GraphQLID },
    floorName: { type: GraphQLString },
    floorDescription: { type: GraphQLString },
    rooms: {
      type: new GraphQLList(RoomType),
      resolve(parentValue, args) {
        return axios
          .get(`http://localhost:3000/floors/${parentValue.id}/rooms`)
          .then((resp) => resp.data);
      }
    }
  })
});

const AssetType = new GraphQLObjectType({
  name: 'AssetType',
  fields: () => ({
    id: { type: GraphQLID },
    category: { type: GraphQLString },
    assetName: { type: GraphQLString },
    assetNumber: { type: GraphQLString },
    roomId: { type: GraphQLString },
    location: { type: GraphQLString },
    isHeritageAsset: { type: GraphQLBoolean },
    hasImages: { type: GraphQLBoolean },
    hasGLTF: { type: GraphQLBoolean },
    hasPhotogrammetry: { type: GraphQLBoolean },
    hasPointClouds: { type: GraphQLBoolean },
    assetDescription: { type: GraphQLString },
    room: {
      type: RoomType,
      resolve(parentValue, args) {
        return axios
          .get(`http://localhost:3000/rooms/${parentValue.roomId}`)
          .then((resp) => resp.data);
      }
    }
  })
});

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    getRoom: {
      type: RoomType,
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(parentValue, { id }) {
        return axios.get(`http://localhost:3000/rooms/${id}`).then((resp) => resp.data);
      }
    },
    getFloor: {
      type: FloorType,
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(parentValue, { id }) {
        return axios.get(`http://localhost:3000/floors/${id}`).then((resp) => resp.data); //to make it compatible between axios and graphql, a workaround
      }
    },
    getAsset: {
      type: AssetType,
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(parentValue, { id }) {
        return axios.get(`http://localhost:3000/assets/${id}`).then((resp) => resp.data); //to make it compatible between axios and graphql, a workaround
      }
    },
    getAllRooms: {
      type: new GraphQLList(RoomType),
      resolve() {
        return axios.get(`http://localhost:3000/rooms`).then((resp) => resp.data);
      }
    },
    getAllAssets: {
      type: new GraphQLList(AssetType),
      resolve() {
        return axios.get(`http://localhost:3000/assets`).then((resp) => resp.data);
      }
    },
    getAllFloors: {
      type: new GraphQLList(FloorType),
      resolve() {
        return axios.get(`http://localhost:3000/floors`).then((resp) => resp.data);
      }
    }
  }
});

//expose this to the rest of the application
module.exports = new GraphQLSchema({
  query: RootQuery
});

对于 Elasticsearch,版本是 7.0。我在 上运行localhost:9200,我有 3 个索引,roomsfloorsassets,每个索引都有一个映射。我试着这样编码:

const graphql = require('graphql');
const graphql_compose_elasticsearch = require('graphql-compose-elasticsearch');
const { elasticApiFieldConfig, composeWithElastic } = graphql_compose_elasticsearch;
const { GraphQLSchema, GraphQLObjectType } = graphql;
const elasticsearch = require('elasticsearch');

// Mapping obtained from ElasticSearch server
const floor_mapping = {
  properties: {
    floorId: {
      type: 'text',
      fields: {
        keyword: {
          type: 'keyword',
          ignore_above: 256
        }
      }
    },
    hasGLTF: {
      type: 'boolean'
    },
    hasImages: {
      type: 'boolean'
    },
    hasPhotogrammetry: {
      type: 'boolean'
    },
    hasPointClouds: {
      type: 'boolean'
    },
    roomDescription: {
      type: 'text',
      fields: {
        keyword: {
          type: 'keyword',
          ignore_above: 256
        }
      }
    },
    roomName: {
      type: 'text',
      fields: {
        keyword: {
          type: 'keyword',
          ignore_above: 256
        }
      }
    },
    roomNumber: {
      type: 'text',
      fields: {
        keyword: {
          type: 'keyword',
          ignore_above: 256
        }
      }
    }
  }
};

const room_mapping = {
  //similar
};

const asset_mapping = {
 //similar
};

const Room = composeWithElastic({
  graphqlTypeName: 'RoomType',
  elasticIndex: 'rooms',
  elasticType: '_doc',
  elasticMapping: room_mapping,
  elasticClient: new elasticsearch.Client({
    host: 'http://localhost:9200',
    apiVersion: '7.0'
  })
});

const Floor = composeWithElastic({
  graphqlTypeName: 'FloorType',
  elasticIndex: 'floors',
  elasticType: '_doc',
  elasticMapping: floor_mapping,
  elasticClient: new elasticsearch.Client({
    host: 'http://localhost:9200',
    apiVersion: '7.0'
  })
});

const Asset = composeWithElastic({
  graphqlTypeName: 'AssetType',
  elasticIndex: 'assets',
  elasticType: '_doc',
  elasticMapping: asset_mapping,
  elasticClient: new elasticsearch.Client({
    host: 'http://localhost:9200',
    apiVersion: '7.0'
  })
});

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      roomSearch: Room.getResolver('search').getFieldConfig(),
      roomSearchConnection: Room.getResolver('searchConnection').getFieldConfig(),
      elastic70: elasticApiFieldConfig({
        host: 'http://localhost:9200',
        apiVersion: '7.0'
      })
    }
  })
});

module.default = { schema };

server.js 是这样的:

const graphqlHTTP = require('express-graphql');
const { schema } = require('./schema/schema_es');
// const schema = require('./schema/schema');
var cors = require('cors');
const server = require('express')();

//allow cross-origin
server.use(
  '/',
  cors(),
  graphqlHTTP({
    schema: schema,
    graphiql: true,
    formatError: (error) => ({
      message: error.message,
      stack: error.stack.split('\n')
    })
  })
);

const PORT = process.env.PORT || 6060;
server.listen(PORT, () => console.log(`Server started on port ${PORT}`));

我重新启动了服务器,这是我得到的错误 invariant.esm.js:31 [GraphQL error]: Message: GraphQL middleware options must contain a schema., Location: undefined, Path: undefined

我不知道如何使用此方法将 graphql 模式、rootquery..etc 转换为 ES 查询graphql-compose-elasticsearch,不胜感激!

标签: node.jselasticsearchgraphql

解决方案


推荐阅读