├── src
├── assets
│ └── sass
│ │ ├── _general.scss
│ │ ├── _mixines.sass
│ │ ├── _common.sass
│ │ └── _variables.scss
├── components
│ ├── App
│ │ ├── _styles.scss
│ │ └── index.jsx
│ └── index.js
├── services
│ ├── index.js
│ └── requestAPI.js
├── index.html
└── index.js
├── .gitignore
├── server
├── server.es.schema.js
├── server.client.js
├── server.es.delete.js
├── server.es.create.js
├── server.es.bulk.js
├── server.elasticsearch.js
├── server.graphql.js
├── server.js
└── json
│ ├── es.settings-mappings.json
│ └── books.json
├── .babelrc
├── README.md
├── .eslintrc.json
├── webpack.config.js
└── package.json
/src/assets/sass/_general.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/sass/_mixines.sass:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/App/_styles.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | export * from "./App";
--------------------------------------------------------------------------------
/src/services/index.js:
--------------------------------------------------------------------------------
1 | export * from "./requestAPI";
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | /node_modules
4 | /*.lock
5 | *.lock
6 | /*.http
7 | *.http
8 |
--------------------------------------------------------------------------------
/server/server.es.schema.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "size": 1000,
3 | "from": 0,
4 | "query": {
5 | "match_all": {}
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/src/assets/sass/_common.sass:
--------------------------------------------------------------------------------
1 | //@import "~bootstrap/scss/bootstrap-reboot"
2 | //@import "~bootstrap/scss/bootstrap-grid"
3 | @import "~bootstrap/scss/bootstrap"
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | ["@babel/plugin-proposal-class-properties", { "loose": true }],
8 | ]
9 | }
--------------------------------------------------------------------------------
/server/server.client.js:
--------------------------------------------------------------------------------
1 | const ElasticSearch = require('elasticsearch');
2 |
3 | /**
4 | * *** ElasticSearch *** client
5 | * @type {Client}
6 | */
7 | const client = new ElasticSearch.Client({
8 | hosts: ['http://127.0.0.1:9200']
9 | });
10 |
11 | module.exports = client;
12 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | App
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/server/server.es.delete.js:
--------------------------------------------------------------------------------
1 | const client = require('./server.client');
2 |
3 | client.indices.delete(
4 | {
5 | index: "catalog"
6 | },
7 | (error, response, status) => {
8 | if(!error) {
9 | console.info("🚀 Deleted index");
10 | console.info(response);
11 | } else {
12 | console.info(error);
13 | }
14 |
15 | }
16 | );
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚀 ElasticSearch, GraphQL, React, Node+Express🤯! How ?
2 | Simple application using React+GraphQL, ElasticSearch as full-text search and Node JS
3 | [medium.com/@andrewrymaruk](https://medium.com/@andrewrymaruk/elasticsearch-graphql-react-node-express-how-cb2c2cc708f8)
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/server/server.es.create.js:
--------------------------------------------------------------------------------
1 | const client = require('./server.client');
2 | const params = require('./json/es.settings-mappings');
3 |
4 | client.indices.create(
5 | {
6 | index: "catalog",
7 | body: params
8 | },
9 | (error, response, status) => {
10 | if(!error) {
11 | console.info("\n🚀 Created a new index");
12 | console.info(response);
13 | console.info('\n');
14 | } else {
15 | console.info(error);
16 | }
17 |
18 | }
19 | );
20 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 6,
4 | "sourceType": "module",
5 | "ecmaFeatures": {
6 | "jsx": true
7 | }
8 | },
9 | "parser": "babel-eslint",
10 | "plugins": [
11 | "react-hooks"
12 | ],
13 | "rules": {
14 | "semi": "error",
15 | "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
16 | "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
17 | }
18 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { App } from "./components";
4 |
5 | import { ApolloProvider } from 'react-apollo'
6 | import { ApolloClient } from 'apollo-client'
7 | import { createHttpLink } from 'apollo-link-http'
8 | import { InMemoryCache } from 'apollo-cache-inmemory'
9 |
10 | const httpLink = createHttpLink({
11 | uri: 'http://localhost:9100/graphql'
12 | });
13 |
14 | const client = new ApolloClient({
15 | link: httpLink,
16 | cache: new InMemoryCache()
17 | });
18 |
19 | ReactDOM.render(
20 | ,
21 | document.getElementById("root")
22 | );
23 |
--------------------------------------------------------------------------------
/src/services/requestAPI.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const AxiosInstanse = axios.create({
4 | baseURL: 'http://localhost:9100/api',
5 | proxyHeaders: false,
6 | credentials: true,
7 | crossdomain: true,
8 | headers: {
9 | 'Content-Type': 'application/json'
10 | },
11 | });
12 |
13 | const AxiosClient = {
14 | method: null,
15 | url: null,
16 | data: {}
17 | };
18 |
19 | export const requestAPI = args => (
20 | new Promise((resolve, reject) => {
21 | AxiosClient = {...AxiosClient, ...args};
22 | AxiosClient.data = args.body;
23 | AxiosInstanse.request(AxiosClient)
24 | .then(r => resolve(r))
25 | .catch(e => reject(e));
26 | })
27 | );
28 |
--------------------------------------------------------------------------------
/server/server.es.bulk.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const client = require('./server.client');
3 | const PRODUCTS = require('./json/books.json').products;
4 |
5 | /**
6 | * Generate own bulk schema:
7 | * {index: {_index: "catalog"}}
8 | * {name: "Some name of product", brand: "Name Brand", ...}
9 | */
10 | let initialBulk = {index: {_index: "catalog"}};
11 | let collectionBulk = [];
12 | _.map(_.keys(PRODUCTS), uuid => {
13 | collectionBulk = [
14 | ...collectionBulk,
15 | initialBulk,
16 | PRODUCTS[uuid]
17 | ];
18 | });
19 |
20 | client.bulk({body: collectionBulk}, function (err, r) {
21 | if (err) {
22 | console.log(`Failed Bulk operation\n`, err);
23 | } else {
24 | console.log(`🚀 Successfully imported ${_.keys(PRODUCTS).length} items \n`);
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/src/assets/sass/_variables.scss:
--------------------------------------------------------------------------------
1 | $blue: #027bf3;
2 | $blue-hover: #028aff;
3 | $blue-d: #0463c2;
4 | $red: #e12828;
5 | $green: #30dc45;
6 | $white: #fff;
7 | $gray-vl:#fafafa;
8 | $gray-l:#f9f9f9;
9 | $gray-m:#f1f1f1;
10 | $gray-d:#ebebeb;
11 | $radius:2px;
12 |
13 | $color_panel :rgba(49, 49, 49, 0.94);
14 | $color_logo :rgba(0, 0, 0, 0.8);
15 | $color_bg :#F2F2F2;
16 | $color_orange :#ff7a43;
17 | $green :#2eab81;
18 | $green_dark :#22946e;
19 | $green_shadow :#24946f;
20 | $green_light :#30cf9a;
21 | $gray :#e2e2e2;
22 | $gray_shadow :#bebebe;
23 | $gray_light :#e9e9e9;
24 | $blue :#568fde;
25 | $blue_dark :#275fad;
26 | $blue_shadow :#275fad;
27 | $blue_light :#6cacff;
28 | $violet :#7786ff;
29 | $red_light : #ee4141;
30 | $red_error: #ff9ba2;
31 | $shadow: 0px 2px 0px;
32 | $shadow-btn: 1px 0px 0px;
33 |
--------------------------------------------------------------------------------
/server/server.elasticsearch.js:
--------------------------------------------------------------------------------
1 | const client = require('./server.client');
2 | const elasticSearchSchema = require('./server.es.schema');
3 |
4 | /**
5 | * TODO Ping the CLIENT to be sure
6 | * *** ElasticSearch *** is up
7 | */
8 | client.ping({
9 | requestTimeout: 30000,
10 | }, function (error) {
11 | error
12 | ? console.error('ElasticSearch cluster is down!')
13 | : console.log('ElasticSearch is ok');
14 | });
15 |
16 | function ElasticSearchClient(body) {
17 | // perform the actual search passing in the index, the search query and the type
18 | return client.search({index: 'catalog', body: body});
19 | }
20 |
21 | function ApiElasticSearchClient(req, res) {
22 | // perform the actual search passing in the index, the search query and the type
23 | ElasticSearchClient({...elasticSearchSchema})
24 | .then(r => res.send(r['hits']['hits']))
25 | .catch(e => {
26 | console.error(e);
27 | res.send([]);
28 | });
29 | }
30 |
31 | module.exports = {
32 | ApiElasticSearchClient,
33 | ElasticSearchClient
34 | };
35 |
--------------------------------------------------------------------------------
/src/components/App/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Query } from 'react-apollo';
3 | import gql from 'graphql-tag';
4 | import * as _ from "lodash";
5 |
6 | const PRODUCTS = gql`{
7 | products {
8 | }
9 | }`;
10 |
11 | export class App extends Component {
12 | render() {
13 | return (
14 |
15 | {
16 | ({ loading, error, data }) => (
17 | loading
18 | ? (<>Loading...>)
19 | : (
20 | {
21 | data.products && _.map(data.products, (item, i) =>{
22 | return (
23 | - {
24 | _.map(_.keys(item), (key, j) =>
25 | key !== '__typename' && {item[key]}
26 | )
27 | }
28 | );
29 | })
30 | }
31 | )
32 | )
33 | }
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/server/server.graphql.js:
--------------------------------------------------------------------------------
1 | const {ElasticSearchClient} = require('./server.elasticsearch');
2 | const elasticSearchSchema = require('./server.es.schema');
3 | const {makeExecutableSchema} = require('graphql-tools');
4 |
5 | // Construct a schema, using GraphQL schema language
6 | const typeDefs = `
7 | type inOthers {
8 | name: String!
9 | quantity: String!
10 | uuid: String!
11 | }
12 |
13 | type inStocks {
14 | name: String!
15 | quantity: String!
16 | uuid: String!
17 | }
18 |
19 | type Product {
20 | name: String
21 | default_image: String
22 | new_product: Boolean
23 | brand: String!
24 | promos: [String]
25 | article: String
26 | promotion: Boolean
27 | price: String
28 | in_stocks: [inStocks]
29 | in_others: inOthers
30 | in_waiting: inOthers
31 | currency_name: String
32 | }
33 |
34 | type Query {
35 | products: [Product]
36 | }
37 | `;
38 |
39 | // The root provides a resolver function for each API endpoint
40 | const resolvers = {
41 | Query: {
42 | products: () => new Promise((resolve, reject) => {
43 | ElasticSearchClient({...elasticSearchSchema})
44 | .then(r => {
45 | let _source = r['hits']['hits'];
46 | _source.map((item, i) => _source[i] = item._source);
47 |
48 | resolve(_source);
49 | });
50 | }),
51 | }
52 | };
53 |
54 | module.exports = makeExecutableSchema({
55 | "typeDefs": [typeDefs],
56 | "resolvers": resolvers
57 | });
58 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const bodyParser = require('body-parser');
4 | const path = require('path');
5 | const {ApolloServer} = require('apollo-server-express');
6 | const {ApiElasticSearchClient} = require('./server.elasticsearch');
7 | const madeExecutableSchema = require('./server.graphql');
8 |
9 | // PORT
10 | const PORT = 9100;
11 |
12 | const server = new ApolloServer({
13 | schema: madeExecutableSchema,
14 | playground: true,
15 | });
16 |
17 | // TODO Use the BodyParser as a middleware
18 | app.use(bodyParser.json());
19 |
20 | // TODO Set port for the app to listen on
21 | app.set('port', process.env.PORT || 3001);
22 |
23 | // TODO Set path to serve static files
24 | app.use(express.static(path.join(__dirname, 'public')));
25 |
26 | // TODO Enable CORS
27 | app.use(function (req, res, next) {
28 | res.header("Access-Control-Allow-Origin", "*");
29 | res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
30 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Headers, Access-Control-Request-Method");
31 | next();
32 | });
33 |
34 | // Define the `/search` route that should return elastic search results
35 | app.get('/search', ApiElasticSearchClient);
36 |
37 | server.applyMiddleware({app});
38 |
39 | app.listen(PORT, function () {
40 | console.log(`Express server listening on port :${PORT}${server.graphqlPath}`);
41 | });
42 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const HtmlWebpackPlugin = require("html-webpack-plugin");
3 | const ExtractTextPlugin = require("extract-text-webpack-plugin");
4 | const webpack = require("webpack");
5 | const glob = require("glob");
6 |
7 | // const devMode = process.env.NODE_ENV !== 'production';
8 |
9 | module.exports = {
10 |
11 | entry: {
12 | js: [
13 | "@babel/polyfill",
14 | path.resolve(__dirname, "./src/index.js"),
15 | ...glob.sync("./src/**/*.s*ss")
16 | ]
17 | },
18 |
19 | output: {
20 | path: path.join(__dirname, "/dist"),
21 | publicPath: "/",
22 | filename: "js/[name].bundle.js"
23 | },
24 |
25 | module: {
26 | rules: [
27 |
28 | {
29 | test: /\.(js|jsx)$/,
30 | exclude: /node_modules/,
31 | use: ["babel-loader"]
32 | },
33 |
34 | {
35 | test: /\.(sass|scss)$/,
36 | exclude: /node_modules/,
37 | use: ExtractTextPlugin.extract({
38 | fallback: 'style-loader',
39 | use: [
40 | // {loader: "react-web-component-style-loader"},
41 | {loader: 'css-loader'},
42 | {
43 | loader: 'postcss-loader',
44 | options: {
45 | plugins: loader => [
46 | require('autoprefixer'),
47 | ]
48 | }
49 | },
50 | {loader: 'sass-loader'}]
51 | })
52 | },
53 |
54 | {
55 | test: /\.css$/,
56 | use: [
57 | {loader: "react-web-component-style-loader"},
58 | {loader: "css-loader"}
59 | ]
60 | },
61 |
62 | ]
63 | },
64 |
65 | resolve: {
66 | extensions: ['.js', '.jsx'],
67 | },
68 |
69 | plugins: [
70 | new webpack.HotModuleReplacementPlugin(),
71 |
72 | new HtmlWebpackPlugin(),
73 |
74 | new ExtractTextPlugin({
75 | filename: 'css/styles.css'
76 | }),
77 |
78 | new HtmlWebpackPlugin({
79 | template: "./src/index.html",
80 | filename: "index.html",
81 | path: path.resolve(__dirname, '')
82 | }),
83 |
84 | ],
85 |
86 | devServer: {
87 | publicPath: '/',
88 | historyApiFallback: true,
89 | inline: true,
90 | port: 9999
91 | },
92 | };
93 |
--------------------------------------------------------------------------------
/server/json/es.settings-mappings.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "number_of_shards" : 1,
4 | "max_ngram_diff": 99,
5 | "max_shingle_diff": 99,
6 | "analysis": {
7 | "analyzer": {
8 | "brand_analyzer": {
9 | "tokenizer": "whitespace",
10 | "filter": [
11 | "lowercase",
12 | "trim"
13 | ]
14 | },
15 | "catalog_analyzer": {
16 | "tokenizer": "whitespace",
17 | "filter": [
18 | "lowercase"
19 | ]
20 | },
21 | "article_analyzer": {
22 | "tokenizer": "articule_tokenizer",
23 | "filter": [
24 | "lowercase"
25 | ]
26 | }
27 | },
28 | "tokenizer": {
29 | "catalog_tokenizer": {
30 | "type": "ngram",
31 | "min_gram": 4,
32 | "max_gram": 32,
33 | "token_chars": [
34 | "letter",
35 | "digit"
36 | ]
37 | },
38 | "articule_tokenizer": {
39 | "type": "ngram",
40 | "min_gram": 3,
41 | "max_gram": 24
42 | },
43 | "brand_tokenizer": {
44 | "type": "ngram",
45 | "min_gram": 2,
46 | "max_gram": 12,
47 | "token_chars": [
48 | "letter",
49 | "digit"
50 | ]
51 | },
52 | "nodes_tokenizer": {
53 | "type": "path_hierarchy",
54 | "delimiter": "\\",
55 | "replacement": "/"
56 | },
57 | "path_tokenizer": {
58 | "type": "path_hierarchy",
59 | "delimiter": "\\",
60 | "replacement": "/"
61 | }
62 | }
63 | }
64 | },
65 | "mappings": {
66 | "properties": {
67 | "uuid": {
68 | "type": "text"
69 | },
70 | "article": {
71 | "type": "text",
72 | "analyzer": "article_analyzer",
73 | "fielddata": true,
74 | "fields": {
75 | "raw": {
76 | "type": "keyword"
77 | }
78 | }
79 | },
80 | "brand": {
81 | "type": "text",
82 | "fielddata": true,
83 | "analyzer": "brand_analyzer",
84 | "fields": {
85 | "raw": {
86 | "type": "keyword"
87 | }
88 | }
89 | },
90 | "name": {
91 | "type": "text",
92 | "fielddata": true,
93 | "analyzer": "catalog_analyzer",
94 | "fields": {
95 | "raw": {
96 | "type": "keyword"
97 | }
98 | }
99 | }
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-webpack",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "eslintConfig": ".eslintrc.json",
7 | "scripts": {
8 | "start": "webpack-dev-server --mode development --open --hot",
9 | "build": "webpack --config webpack.config.js --mode production",
10 | "storybook": "start-storybook -p 9001 -c .storybook",
11 | "sb": "start-storybook -p 9001 -c .storybook",
12 | "es:create": "node ./server/server.es.create.js && node ./server/server.es.bulk.js",
13 | "es:delete": "node ./server/server.es.delete.js"
14 | },
15 | "postcss": {},
16 | "keywords": [],
17 | "author": "",
18 | "license": "ISC",
19 | "devDependencies": {
20 | "@babel/core": "^7.5.5",
21 | "@babel/plugin-proposal-class-properties": "^7.5.5",
22 | "@babel/polyfill": "^7.4.4",
23 | "@babel/preset-env": "^7.5.5",
24 | "@babel/preset-react": "^7.0.0",
25 | "@sambego/storybook-styles": "^1.0.0",
26 | "@storybook/addon-actions": "^5.1.10",
27 | "@storybook/addon-console": "^1.2.1",
28 | "@storybook/addon-info": "^5.1.10",
29 | "@storybook/addon-knobs": "^5.1.10",
30 | "@storybook/addon-links": "^5.1.10",
31 | "@storybook/addon-notes": "^5.1.10",
32 | "@storybook/addon-options": "^5.1.10",
33 | "@storybook/addon-storysource": "^5.1.10",
34 | "@storybook/addons": "^5.1.10",
35 | "@storybook/react": "^5.1.10",
36 | "apollo-boost": "^0.4.3",
37 | "apollo-server-express": "^2.8.1",
38 | "autoprefixer": "^9.6.1",
39 | "axios": "^0.19.0",
40 | "babel-eslint": "^10.0.3",
41 | "babel-loader": "^8.0.0-beta.6",
42 | "babel-plugin-react-hooks-hot-load": "^1.2.0",
43 | "babel-polyfill": "^6.26.0",
44 | "babel-preset-es2015": "^6.24.1",
45 | "body-parser": "^1.19.0",
46 | "classnames": "^2.2.6",
47 | "clean-webpack-plugin": "^3.0.0",
48 | "cors": "^2.8.5",
49 | "cross-env": "^5.2.0",
50 | "css-loader": "^3.2.0",
51 | "elasticsearch": "^16.3.0",
52 | "eslint": "^6.1.0",
53 | "eslint-config-airbnb": "^17.1.1",
54 | "eslint-plugin-react-hooks": "^1.6.1",
55 | "express": "^4.17.1",
56 | "express-graphql": "^0.9.0",
57 | "extract-text-webpack-plugin": "^4.0.0-beta.0",
58 | "file-loader": "^4.2.0",
59 | "fs": "^0.0.1-security",
60 | "glob": "^7.1.4",
61 | "graphql": "^14.4.2",
62 | "graphql-tag": "^2.10.1",
63 | "graphql-tools": "^4.0.5",
64 | "html-webpack-plugin": "^3.2.0",
65 | "image-webpack-loader": "^5.0.0",
66 | "metro-babel7-plugin-react-transform": "^0.54.1",
67 | "node-sass": "^4.12.0",
68 | "path": "^0.12.7",
69 | "postcss-loader": "^3.0.0",
70 | "postcss-smart-import": "^0.7.6",
71 | "prop-types": "^15.6.2",
72 | "raw-loader": "^3.1.0",
73 | "react-apollo": "^3.0.0",
74 | "react-highlight": "^0.12.0",
75 | "react-redux": "^7.1.0",
76 | "react-script": "^2.0.5",
77 | "react-web-component": "^1.3.0",
78 | "react-web-component-style-loader": "^0.1.4-alpha",
79 | "redux": "^4.0.4",
80 | "request": "^2.88.0",
81 | "sass-loader": "^7.1.0",
82 | "storybook-addon-code": "^0.1.4",
83 | "storybook-addon-jsx": "^7.1.5",
84 | "storybook-readme": "^5.0.6",
85 | "style-loader": "^1.0.0",
86 | "webpack": "^4.39.1",
87 | "webpack-cli": "^3.3.6",
88 | "webpack-dev-server": "^3.7.2"
89 | },
90 | "dependencies": {
91 | "@webcomponents/webcomponentsjs": "^2.2.10",
92 | "bootstrap": "^4.2.1",
93 | "jquery": "^3.4.1",
94 | "lodash": "^4.17.15",
95 | "react": "^16.9.0-rc.0",
96 | "react-dom": "^16.9.0-rc.0",
97 | "react-hot-loader": "^4.12.10"
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/server/json/books.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "brand": "Chinua Achebe",
5 | "country": "Nigeria",
6 | "imageLink": "images/things-fall-apart.jpg",
7 | "language": "English",
8 | "link": "https://en.wikipedia.org/wiki/Things_Fall_Apart\n",
9 | "pages": 209,
10 | "name": "Things Fall Apart",
11 | "article": 1958
12 | },
13 | {
14 | "brand": "Hans Christian Andersen",
15 | "country": "Denmark",
16 | "imageLink": "images/fairy-tales.jpg",
17 | "language": "Danish",
18 | "link": "https://en.wikipedia.org/wiki/Fairy_Tales_Told_for_Children._First_Collection.\n",
19 | "pages": 784,
20 | "name": "Fairy tales",
21 | "article": 1836
22 | },
23 | {
24 | "brand": "Dante Alighieri",
25 | "country": "Italy",
26 | "imageLink": "images/the-divine-comedy.jpg",
27 | "language": "Italian",
28 | "link": "https://en.wikipedia.org/wiki/Divine_Comedy\n",
29 | "pages": 928,
30 | "name": "The Divine Comedy",
31 | "article": 1315
32 | },
33 | {
34 | "brand": "Unknown",
35 | "country": "Sumer and Akkadian Empire",
36 | "imageLink": "images/the-epic-of-gilgamesh.jpg",
37 | "language": "Akkadian",
38 | "link": "https://en.wikipedia.org/wiki/Epic_of_Gilgamesh\n",
39 | "pages": 160,
40 | "name": "The Epic Of Gilgamesh",
41 | "article": -1700
42 | },
43 | {
44 | "brand": "Unknown",
45 | "country": "Achaemenid Empire",
46 | "imageLink": "images/the-book-of-job.jpg",
47 | "language": "Hebrew",
48 | "link": "https://en.wikipedia.org/wiki/Book_of_Job\n",
49 | "pages": 176,
50 | "name": "The Book Of Job",
51 | "article": -600
52 | },
53 | {
54 | "brand": "Unknown",
55 | "country": "India/Iran/Iraq/Egypt/Tajikistan",
56 | "imageLink": "images/one-thousand-and-one-nights.jpg",
57 | "language": "Arabic",
58 | "link": "https://en.wikipedia.org/wiki/One_Thousand_and_One_Nights\n",
59 | "pages": 288,
60 | "name": "One Thousand and One Nights",
61 | "article": 1200
62 | },
63 | {
64 | "brand": "Unknown",
65 | "country": "Iceland",
66 | "imageLink": "images/njals-saga.jpg",
67 | "language": "Old Norse",
68 | "link": "https://en.wikipedia.org/wiki/Nj%C3%A1ls_saga\n",
69 | "pages": 384,
70 | "name": "Nj\u00e1l's Saga",
71 | "article": 1350
72 | },
73 | {
74 | "brand": "Jane Austen",
75 | "country": "United Kingdom",
76 | "imageLink": "images/pride-and-prejudice.jpg",
77 | "language": "English",
78 | "link": "https://en.wikipedia.org/wiki/Pride_and_Prejudice\n",
79 | "pages": 226,
80 | "name": "Pride and Prejudice",
81 | "article": 1813
82 | },
83 | {
84 | "brand": "Honor\u00e9 de Balzac",
85 | "country": "France",
86 | "imageLink": "images/le-pere-goriot.jpg",
87 | "language": "French",
88 | "link": "https://en.wikipedia.org/wiki/Le_P%C3%A8re_Goriot\n",
89 | "pages": 443,
90 | "name": "Le P\u00e8re Goriot",
91 | "article": 1835
92 | },
93 | {
94 | "brand": "Samuel Beckett",
95 | "country": "Republic of Ireland",
96 | "imageLink": "images/molloy-malone-dies-the-unnamable.jpg",
97 | "language": "French, English",
98 | "link": "https://en.wikipedia.org/wiki/Molloy_(novel)\n",
99 | "pages": 256,
100 | "name": "Molloy, Malone Dies, The Unnamable, the trilogy",
101 | "article": 1952
102 | },
103 | {
104 | "brand": "Giovanni Boccaccio",
105 | "country": "Italy",
106 | "imageLink": "images/the-decameron.jpg",
107 | "language": "Italian",
108 | "link": "https://en.wikipedia.org/wiki/The_Decameron\n",
109 | "pages": 1024,
110 | "name": "The Decameron",
111 | "article": 1351
112 | },
113 | {
114 | "brand": "Jorge Luis Borges",
115 | "country": "Argentina",
116 | "imageLink": "images/ficciones.jpg",
117 | "language": "Spanish",
118 | "link": "https://en.wikipedia.org/wiki/Ficciones\n",
119 | "pages": 224,
120 | "name": "Ficciones",
121 | "article": 1965
122 | },
123 | {
124 | "brand": "Emily Bront\u00eb",
125 | "country": "United Kingdom",
126 | "imageLink": "images/wuthering-heights.jpg",
127 | "language": "English",
128 | "link": "https://en.wikipedia.org/wiki/Wuthering_Heights\n",
129 | "pages": 342,
130 | "name": "Wuthering Heights",
131 | "article": 1847
132 | },
133 | {
134 | "brand": "Albert Camus",
135 | "country": "Algeria, French Empire",
136 | "imageLink": "images/l-etranger.jpg",
137 | "language": "French",
138 | "link": "https://en.wikipedia.org/wiki/The_Stranger_(novel)\n",
139 | "pages": 185,
140 | "name": "The Stranger",
141 | "article": 1942
142 | },
143 | {
144 | "brand": "Paul Celan",
145 | "country": "Romania, France",
146 | "imageLink": "images/poems-paul-celan.jpg",
147 | "language": "German",
148 | "link": "\n",
149 | "pages": 320,
150 | "name": "Poems",
151 | "article": 1952
152 | },
153 | {
154 | "brand": "Louis-Ferdinand C\u00e9line",
155 | "country": "France",
156 | "imageLink": "images/voyage-au-bout-de-la-nuit.jpg",
157 | "language": "French",
158 | "link": "https://en.wikipedia.org/wiki/Journey_to_the_End_of_the_Night\n",
159 | "pages": 505,
160 | "name": "Journey to the End of the Night",
161 | "article": 1932
162 | },
163 | {
164 | "brand": "Miguel de Cervantes",
165 | "country": "Spain",
166 | "imageLink": "images/don-quijote-de-la-mancha.jpg",
167 | "language": "Spanish",
168 | "link": "https://en.wikipedia.org/wiki/Don_Quixote\n",
169 | "pages": 1056,
170 | "name": "Don Quijote De La Mancha",
171 | "article": 1610
172 | },
173 | {
174 | "brand": "Geoffrey Chaucer",
175 | "country": "England",
176 | "imageLink": "images/the-canterbury-tales.jpg",
177 | "language": "English",
178 | "link": "https://en.wikipedia.org/wiki/The_Canterbury_Tales\n",
179 | "pages": 544,
180 | "name": "The Canterbury Tales",
181 | "article": 1450
182 | },
183 | {
184 | "brand": "Anton Chekhov",
185 | "country": "Russia",
186 | "imageLink": "images/stories-of-anton-chekhov.jpg",
187 | "language": "Russian",
188 | "link": "https://en.wikipedia.org/wiki/List_of_short_stories_by_Anton_Chekhov\n",
189 | "pages": 194,
190 | "name": "Stories",
191 | "article": 1886
192 | },
193 | {
194 | "brand": "Joseph Conrad",
195 | "country": "United Kingdom",
196 | "imageLink": "images/nostromo.jpg",
197 | "language": "English",
198 | "link": "https://en.wikipedia.org/wiki/Nostromo\n",
199 | "pages": 320,
200 | "name": "Nostromo",
201 | "article": 1904
202 | },
203 | {
204 | "brand": "Charles Dickens",
205 | "country": "United Kingdom",
206 | "imageLink": "images/great-expectations.jpg",
207 | "language": "English",
208 | "link": "https://en.wikipedia.org/wiki/Great_Expectations\n",
209 | "pages": 194,
210 | "name": "Great Expectations",
211 | "article": 1861
212 | },
213 | {
214 | "brand": "Denis Diderot",
215 | "country": "France",
216 | "imageLink": "images/jacques-the-fatalist.jpg",
217 | "language": "French",
218 | "link": "https://en.wikipedia.org/wiki/Jacques_the_Fatalist\n",
219 | "pages": 596,
220 | "name": "Jacques the Fatalist",
221 | "article": 1796
222 | },
223 | {
224 | "brand": "Alfred D\u00f6blin",
225 | "country": "Germany",
226 | "imageLink": "images/berlin-alexanderplatz.jpg",
227 | "language": "German",
228 | "link": "https://en.wikipedia.org/wiki/Berlin_Alexanderplatz\n",
229 | "pages": 600,
230 | "name": "Berlin Alexanderplatz",
231 | "article": 1929
232 | },
233 | {
234 | "brand": "Fyodor Dostoevsky",
235 | "country": "Russia",
236 | "imageLink": "images/crime-and-punishment.jpg",
237 | "language": "Russian",
238 | "link": "https://en.wikipedia.org/wiki/Crime_and_Punishment\n",
239 | "pages": 551,
240 | "name": "Crime and Punishment",
241 | "article": 1866
242 | },
243 | {
244 | "brand": "Fyodor Dostoevsky",
245 | "country": "Russia",
246 | "imageLink": "images/the-idiot.jpg",
247 | "language": "Russian",
248 | "link": "https://en.wikipedia.org/wiki/The_Idiot\n",
249 | "pages": 656,
250 | "name": "The Idiot",
251 | "article": 1869
252 | },
253 | {
254 | "brand": "Fyodor Dostoevsky",
255 | "country": "Russia",
256 | "imageLink": "images/the-possessed.jpg",
257 | "language": "Russian",
258 | "link": "https://en.wikipedia.org/wiki/Demons_(Dostoyevsky_novel)\n",
259 | "pages": 768,
260 | "name": "The Possessed",
261 | "article": 1872
262 | },
263 | {
264 | "brand": "Fyodor Dostoevsky",
265 | "country": "Russia",
266 | "imageLink": "images/the-brothers-karamazov.jpg",
267 | "language": "Russian",
268 | "link": "https://en.wikipedia.org/wiki/The_Brothers_Karamazov\n",
269 | "pages": 824,
270 | "name": "The Brothers Karamazov",
271 | "article": 1880
272 | },
273 | {
274 | "brand": "George Eliot",
275 | "country": "United Kingdom",
276 | "imageLink": "images/middlemarch.jpg",
277 | "language": "English",
278 | "link": "https://en.wikipedia.org/wiki/Middlemarch\n",
279 | "pages": 800,
280 | "name": "Middlemarch",
281 | "article": 1871
282 | },
283 | {
284 | "brand": "Ralph Ellison",
285 | "country": "United States",
286 | "imageLink": "images/invisible-man.jpg",
287 | "language": "English",
288 | "link": "https://en.wikipedia.org/wiki/Invisible_Man\n",
289 | "pages": 581,
290 | "name": "Invisible Man",
291 | "article": 1952
292 | },
293 | {
294 | "brand": "Euripides",
295 | "country": "Greece",
296 | "imageLink": "images/medea.jpg",
297 | "language": "Greek",
298 | "link": "https://en.wikipedia.org/wiki/Medea_(play)\n",
299 | "pages": 104,
300 | "name": "Medea",
301 | "article": -431
302 | },
303 | {
304 | "brand": "William Faulkner",
305 | "country": "United States",
306 | "imageLink": "images/absalom-absalom.jpg",
307 | "language": "English",
308 | "link": "https://en.wikipedia.org/wiki/Absalom,_Absalom!\n",
309 | "pages": 313,
310 | "name": "Absalom, Absalom!",
311 | "article": 1936
312 | },
313 | {
314 | "brand": "William Faulkner",
315 | "country": "United States",
316 | "imageLink": "images/the-sound-and-the-fury.jpg",
317 | "language": "English",
318 | "link": "https://en.wikipedia.org/wiki/The_Sound_and_the_Fury\n",
319 | "pages": 326,
320 | "name": "The Sound and the Fury",
321 | "article": 1929
322 | },
323 | {
324 | "brand": "Gustave Flaubert",
325 | "country": "France",
326 | "imageLink": "images/madame-bovary.jpg",
327 | "language": "French",
328 | "link": "https://en.wikipedia.org/wiki/Madame_Bovary\n",
329 | "pages": 528,
330 | "name": "Madame Bovary",
331 | "article": 1857
332 | },
333 | {
334 | "brand": "Gustave Flaubert",
335 | "country": "France",
336 | "imageLink": "images/l-education-sentimentale.jpg",
337 | "language": "French",
338 | "link": "https://en.wikipedia.org/wiki/Sentimental_Education\n",
339 | "pages": 606,
340 | "name": "Sentimental Education",
341 | "article": 1869
342 | },
343 | {
344 | "brand": "Federico Garc\u00eda Lorca",
345 | "country": "Spain",
346 | "imageLink": "images/gypsy-ballads.jpg",
347 | "language": "Spanish",
348 | "link": "https://en.wikipedia.org/wiki/Gypsy_Ballads\n",
349 | "pages": 218,
350 | "name": "Gypsy Ballads",
351 | "article": 1928
352 | },
353 | {
354 | "brand": "Gabriel Garc\u00eda M\u00e1rquez",
355 | "country": "Colombia",
356 | "imageLink": "images/one-hundred-years-of-solitude.jpg",
357 | "language": "Spanish",
358 | "link": "https://en.wikipedia.org/wiki/One_Hundred_Years_of_Solitude\n",
359 | "pages": 417,
360 | "name": "One Hundred Years of Solitude",
361 | "article": 1967
362 | },
363 | {
364 | "brand": "Gabriel Garc\u00eda M\u00e1rquez",
365 | "country": "Colombia",
366 | "imageLink": "images/love-in-the-time-of-cholera.jpg",
367 | "language": "Spanish",
368 | "link": "https://en.wikipedia.org/wiki/Love_in_the_Time_of_Cholera\n",
369 | "pages": 368,
370 | "name": "Love in the Time of Cholera",
371 | "article": 1985
372 | },
373 | {
374 | "brand": "Johann Wolfgang von Goethe",
375 | "country": "Saxe-Weimar",
376 | "imageLink": "images/faust.jpg",
377 | "language": "German",
378 | "link": "https://en.wikipedia.org/wiki/Goethe%27s_Faust\n",
379 | "pages": 158,
380 | "name": "Faust",
381 | "article": 1832
382 | },
383 | {
384 | "brand": "Nikolai Gogol",
385 | "country": "Russia",
386 | "imageLink": "images/dead-souls.jpg",
387 | "language": "Russian",
388 | "link": "https://en.wikipedia.org/wiki/Dead_Souls\n",
389 | "pages": 432,
390 | "name": "Dead Souls",
391 | "article": 1842
392 | },
393 | {
394 | "brand": "G\u00fcnter Grass",
395 | "country": "Germany",
396 | "imageLink": "images/the-tin-drum.jpg",
397 | "language": "German",
398 | "link": "https://en.wikipedia.org/wiki/The_Tin_Drum\n",
399 | "pages": 600,
400 | "name": "The Tin Drum",
401 | "article": 1959
402 | },
403 | {
404 | "brand": "Jo\u00e3o Guimar\u00e3es Rosa",
405 | "country": "Brazil",
406 | "imageLink": "images/the-devil-to-pay-in-the-backlands.jpg",
407 | "language": "Portuguese",
408 | "link": "https://en.wikipedia.org/wiki/The_Devil_to_Pay_in_the_Backlands\n",
409 | "pages": 494,
410 | "name": "The Devil to Pay in the Backlands",
411 | "article": 1956
412 | },
413 | {
414 | "brand": "Knut Hamsun",
415 | "country": "Norway",
416 | "imageLink": "images/hunger.jpg",
417 | "language": "Norwegian",
418 | "link": "https://en.wikipedia.org/wiki/Hunger_(Hamsun_novel)\n",
419 | "pages": 176,
420 | "name": "Hunger",
421 | "article": 1890
422 | },
423 | {
424 | "brand": "Ernest Hemingway",
425 | "country": "United States",
426 | "imageLink": "images/the-old-man-and-the-sea.jpg",
427 | "language": "English",
428 | "link": "https://en.wikipedia.org/wiki/The_Old_Man_and_the_Sea\n",
429 | "pages": 128,
430 | "name": "The Old Man and the Sea",
431 | "article": 1952
432 | },
433 | {
434 | "brand": "Homer",
435 | "country": "Greece",
436 | "imageLink": "images/the-iliad-of-homer.jpg",
437 | "language": "Greek",
438 | "link": "https://en.wikipedia.org/wiki/Iliad\n",
439 | "pages": 608,
440 | "name": "Iliad",
441 | "article": -735
442 | },
443 | {
444 | "brand": "Homer",
445 | "country": "Greece",
446 | "imageLink": "images/the-odyssey-of-homer.jpg",
447 | "language": "Greek",
448 | "link": "https://en.wikipedia.org/wiki/Odyssey\n",
449 | "pages": 374,
450 | "name": "Odyssey",
451 | "article": -800
452 | },
453 | {
454 | "brand": "Henrik Ibsen",
455 | "country": "Norway",
456 | "imageLink": "images/a-Dolls-house.jpg",
457 | "language": "Norwegian",
458 | "link": "https://en.wikipedia.org/wiki/A_Doll%27s_House\n",
459 | "pages": 68,
460 | "name": "A Doll's House",
461 | "article": 1879
462 | },
463 | {
464 | "brand": "James Joyce",
465 | "country": "Irish Free State",
466 | "imageLink": "images/ulysses.jpg",
467 | "language": "English",
468 | "link": "https://en.wikipedia.org/wiki/Ulysses_(novel)\n",
469 | "pages": 228,
470 | "name": "Ulysses",
471 | "article": 1922
472 | },
473 | {
474 | "brand": "Franz Kafka",
475 | "country": "Czechoslovakia",
476 | "imageLink": "images/stories-of-franz-kafka.jpg",
477 | "language": "German",
478 | "link": "https://en.wikipedia.org/wiki/Franz_Kafka_bibliography#Short_stories\n",
479 | "pages": 488,
480 | "name": "Stories",
481 | "article": 1924
482 | },
483 | {
484 | "brand": "Franz Kafka",
485 | "country": "Czechoslovakia",
486 | "imageLink": "images/the-trial.jpg",
487 | "language": "German",
488 | "link": "https://en.wikipedia.org/wiki/The_Trial\n",
489 | "pages": 160,
490 | "name": "The Trial",
491 | "article": 1925
492 | },
493 | {
494 | "brand": "Franz Kafka",
495 | "country": "Czechoslovakia",
496 | "imageLink": "images/the-castle.jpg",
497 | "language": "German",
498 | "link": "https://en.wikipedia.org/wiki/The_Castle_(novel)\n",
499 | "pages": 352,
500 | "name": "The Castle",
501 | "article": 1926
502 | },
503 | {
504 | "brand": "K\u0101lid\u0101sa",
505 | "country": "India",
506 | "imageLink": "images/the-recognition-of-shakuntala.jpg",
507 | "language": "Sanskrit",
508 | "link": "https://en.wikipedia.org/wiki/Abhij%C3%B1%C4%81na%C5%9B%C4%81kuntalam\n",
509 | "pages": 147,
510 | "name": "The recognition of Shakuntala",
511 | "article": 150
512 | },
513 | {
514 | "brand": "Yasunari Kawabata",
515 | "country": "Japan",
516 | "imageLink": "images/the-sound-of-the-mountain.jpg",
517 | "language": "Japanese",
518 | "link": "https://en.wikipedia.org/wiki/The_Sound_of_the_Mountain\n",
519 | "pages": 288,
520 | "name": "The Sound of the Mountain",
521 | "article": 1954
522 | },
523 | {
524 | "brand": "Nikos Kazantzakis",
525 | "country": "Greece",
526 | "imageLink": "images/zorba-the-greek.jpg",
527 | "language": "Greek",
528 | "link": "https://en.wikipedia.org/wiki/Zorba_the_Greek\n",
529 | "pages": 368,
530 | "name": "Zorba the Greek",
531 | "article": 1946
532 | },
533 | {
534 | "brand": "D. H. Lawrence",
535 | "country": "United Kingdom",
536 | "imageLink": "images/sons-and-lovers.jpg",
537 | "language": "English",
538 | "link": "https://en.wikipedia.org/wiki/Sons_and_Lovers\n",
539 | "pages": 432,
540 | "name": "Sons and Lovers",
541 | "article": 1913
542 | },
543 | {
544 | "brand": "Halld\u00f3r Laxness",
545 | "country": "Iceland",
546 | "imageLink": "images/independent-people.jpg",
547 | "language": "Icelandic",
548 | "link": "https://en.wikipedia.org/wiki/Independent_People\n",
549 | "pages": 470,
550 | "name": "Independent People",
551 | "article": 1934
552 | },
553 | {
554 | "brand": "Giacomo Leopardi",
555 | "country": "Italy",
556 | "imageLink": "images/poems-giacomo-leopardi.jpg",
557 | "language": "Italian",
558 | "link": "\n",
559 | "pages": 184,
560 | "name": "Poems",
561 | "article": 1818
562 | },
563 | {
564 | "brand": "Doris Lessing",
565 | "country": "United Kingdom",
566 | "imageLink": "images/the-golden-notebook.jpg",
567 | "language": "English",
568 | "link": "https://en.wikipedia.org/wiki/The_Golden_Notebook\n",
569 | "pages": 688,
570 | "name": "The Golden Notebook",
571 | "article": 1962
572 | },
573 | {
574 | "brand": "Astrid Lindgren",
575 | "country": "Sweden",
576 | "imageLink": "images/pippi-longstocking.jpg",
577 | "language": "Swedish",
578 | "link": "https://en.wikipedia.org/wiki/Pippi_Longstocking\n",
579 | "pages": 160,
580 | "name": "Pippi Longstocking",
581 | "article": 1945
582 | },
583 | {
584 | "brand": "Lu Xun",
585 | "country": "China",
586 | "imageLink": "images/diary-of-a-madman.jpg",
587 | "language": "Chinese",
588 | "link": "https://en.wikipedia.org/wiki/A_Madman%27s_Diary\n",
589 | "pages": 389,
590 | "name": "Diary of a Madman",
591 | "article": 1918
592 | },
593 | {
594 | "brand": "Naguib Mahfouz",
595 | "country": "Egypt",
596 | "imageLink": "images/children-of-gebelawi.jpg",
597 | "language": "Arabic",
598 | "link": "https://en.wikipedia.org/wiki/Children_of_Gebelawi\n",
599 | "pages": 355,
600 | "name": "Children of Gebelawi",
601 | "article": 1959
602 | },
603 | {
604 | "brand": "Thomas Mann",
605 | "country": "Germany",
606 | "imageLink": "images/buddenbrooks.jpg",
607 | "language": "German",
608 | "link": "https://en.wikipedia.org/wiki/Buddenbrooks\n",
609 | "pages": 736,
610 | "name": "Buddenbrooks",
611 | "article": 1901
612 | },
613 | {
614 | "brand": "Thomas Mann",
615 | "country": "Germany",
616 | "imageLink": "images/the-magic-mountain.jpg",
617 | "language": "German",
618 | "link": "https://en.wikipedia.org/wiki/The_Magic_Mountain\n",
619 | "pages": 720,
620 | "name": "The Magic Mountain",
621 | "article": 1924
622 | },
623 | {
624 | "brand": "Herman Melville",
625 | "country": "United States",
626 | "imageLink": "images/moby-dick.jpg",
627 | "language": "English",
628 | "link": "https://en.wikipedia.org/wiki/Moby-Dick\n",
629 | "pages": 378,
630 | "name": "Moby Dick",
631 | "article": 1851
632 | },
633 | {
634 | "brand": "Michel de Montaigne",
635 | "country": "France",
636 | "imageLink": "images/essais.jpg",
637 | "language": "French",
638 | "link": "https://en.wikipedia.org/wiki/Essays_(Montaigne)\n",
639 | "pages": 404,
640 | "name": "Essays",
641 | "article": 1595
642 | },
643 | {
644 | "brand": "Elsa Morante",
645 | "country": "Italy",
646 | "imageLink": "images/history.jpg",
647 | "language": "Italian",
648 | "link": "https://en.wikipedia.org/wiki/History_(novel)\n",
649 | "pages": 600,
650 | "name": "History",
651 | "article": 1974
652 | },
653 | {
654 | "brand": "Toni Morrison",
655 | "country": "United States",
656 | "imageLink": "images/beloved.jpg",
657 | "language": "English",
658 | "link": "https://en.wikipedia.org/wiki/Beloved_(novel)\n",
659 | "pages": 321,
660 | "name": "Beloved",
661 | "article": 1987
662 | },
663 | {
664 | "brand": "Murasaki Shikibu",
665 | "country": "Japan",
666 | "imageLink": "images/the-tale-of-genji.jpg",
667 | "language": "Japanese",
668 | "link": "https://en.wikipedia.org/wiki/The_Tale_of_Genji\n",
669 | "pages": 1360,
670 | "name": "The Tale of Genji",
671 | "article": 1006
672 | },
673 | {
674 | "brand": "Robert Musil",
675 | "country": "Austria",
676 | "imageLink": "images/the-man-without-qualities.jpg",
677 | "language": "German",
678 | "link": "https://en.wikipedia.org/wiki/The_Man_Without_Qualities\n",
679 | "pages": 365,
680 | "name": "The Man Without Qualities",
681 | "article": 1931
682 | },
683 | {
684 | "brand": "Vladimir Nabokov",
685 | "country": "Russia/United States",
686 | "imageLink": "images/lolita.jpg",
687 | "language": "English",
688 | "link": "https://en.wikipedia.org/wiki/Lolita\n",
689 | "pages": 317,
690 | "name": "Lolita",
691 | "article": 1955
692 | },
693 | {
694 | "brand": "George Orwell",
695 | "country": "United Kingdom",
696 | "imageLink": "images/nineteen-eighty-four.jpg",
697 | "language": "English",
698 | "link": "https://en.wikipedia.org/wiki/Nineteen_Eighty-Four\n",
699 | "pages": 272,
700 | "name": "Nineteen Eighty-Four",
701 | "article": 1949
702 | },
703 | {
704 | "brand": "Ovid",
705 | "country": "Roman Empire",
706 | "imageLink": "images/the-metamorphoses-of-ovid.jpg",
707 | "language": "Classical Latin",
708 | "link": "https://en.wikipedia.org/wiki/Metamorphoses\n",
709 | "pages": 576,
710 | "name": "Metamorphoses",
711 | "article": 100
712 | },
713 | {
714 | "brand": "Fernando Pessoa",
715 | "country": "Portugal",
716 | "imageLink": "images/the-book-of-disquiet.jpg",
717 | "language": "Portuguese",
718 | "link": "https://en.wikipedia.org/wiki/The_Book_of_Disquiet\n",
719 | "pages": 272,
720 | "name": "The Book of Disquiet",
721 | "article": 1928
722 | },
723 | {
724 | "brand": "Edgar Allan Poe",
725 | "country": "United States",
726 | "imageLink": "images/tales-and-poems-of-edgar-allan-poe.jpg",
727 | "language": "English",
728 | "link": "https://en.wikipedia.org/wiki/Edgar_Allan_Poe_bibliography#Tales\n",
729 | "pages": 842,
730 | "name": "Tales",
731 | "article": 1950
732 | },
733 | {
734 | "brand": "Marcel Proust",
735 | "country": "France",
736 | "imageLink": "images/a-la-recherche-du-temps-perdu.jpg",
737 | "language": "French",
738 | "link": "https://en.wikipedia.org/wiki/In_Search_of_Lost_Time\n",
739 | "pages": 2408,
740 | "name": "In Search of Lost Time",
741 | "article": 1920
742 | },
743 | {
744 | "brand": "Fran\u00e7ois Rabelais",
745 | "country": "France",
746 | "imageLink": "images/gargantua-and-pantagruel.jpg",
747 | "language": "French",
748 | "link": "https://en.wikipedia.org/wiki/Gargantua_and_Pantagruel\n",
749 | "pages": 623,
750 | "name": "Gargantua and Pantagruel",
751 | "article": 1533
752 | },
753 | {
754 | "brand": "Juan Rulfo",
755 | "country": "Mexico",
756 | "imageLink": "images/pedro-paramo.jpg",
757 | "language": "Spanish",
758 | "link": "https://en.wikipedia.org/wiki/Pedro_P%C3%A1ramo\n",
759 | "pages": 124,
760 | "name": "Pedro P\u00e1ramo",
761 | "article": 1955
762 | },
763 | {
764 | "brand": "Rumi",
765 | "country": "Sultanate of Rum",
766 | "imageLink": "images/the-masnavi.jpg",
767 | "language": "Persian",
768 | "link": "https://en.wikipedia.org/wiki/Masnavi\n",
769 | "pages": 438,
770 | "name": "The Masnavi",
771 | "article": 1236
772 | },
773 | {
774 | "brand": "Salman Rushdie",
775 | "country": "United Kingdom, India",
776 | "imageLink": "images/midnights-children.jpg",
777 | "language": "English",
778 | "link": "https://en.wikipedia.org/wiki/Midnight%27s_Children\n",
779 | "pages": 536,
780 | "name": "Midnight's Children",
781 | "article": 1981
782 | },
783 | {
784 | "brand": "Saadi",
785 | "country": "Persia, Persian Empire",
786 | "imageLink": "images/bostan.jpg",
787 | "language": "Persian",
788 | "link": "https://en.wikipedia.org/wiki/Bustan_(book)\n",
789 | "pages": 298,
790 | "name": "Bostan",
791 | "article": 1257
792 | },
793 | {
794 | "brand": "Tayeb Salih",
795 | "country": "Sudan",
796 | "imageLink": "images/season-of-migration-to-the-north.jpg",
797 | "language": "Arabic",
798 | "link": "https://en.wikipedia.org/wiki/Season_of_Migration_to_the_North\n",
799 | "pages": 139,
800 | "name": "Season of Migration to the North",
801 | "article": 1966
802 | },
803 | {
804 | "brand": "Jos\u00e9 Saramago",
805 | "country": "Portugal",
806 | "imageLink": "images/blindness.jpg",
807 | "language": "Portuguese",
808 | "link": "https://en.wikipedia.org/wiki/Blindness_(novel)\n",
809 | "pages": 352,
810 | "name": "Blindness",
811 | "article": 1995
812 | },
813 | {
814 | "brand": "William Shakespeare",
815 | "country": "England",
816 | "imageLink": "images/hamlet.jpg",
817 | "language": "English",
818 | "link": "https://en.wikipedia.org/wiki/Hamlet\n",
819 | "pages": 432,
820 | "name": "Hamlet",
821 | "article": 1603
822 | },
823 | {
824 | "brand": "William Shakespeare",
825 | "country": "England",
826 | "imageLink": "images/king-lear.jpg",
827 | "language": "English",
828 | "link": "https://en.wikipedia.org/wiki/King_Lear\n",
829 | "pages": 384,
830 | "name": "King Lear",
831 | "article": 1608
832 | },
833 | {
834 | "brand": "William Shakespeare",
835 | "country": "England",
836 | "imageLink": "images/othello.jpg",
837 | "language": "English",
838 | "link": "https://en.wikipedia.org/wiki/Othello\n",
839 | "pages": 314,
840 | "name": "Othello",
841 | "article": 1609
842 | },
843 | {
844 | "brand": "Sophocles",
845 | "country": "Greece",
846 | "imageLink": "images/oedipus-the-king.jpg",
847 | "language": "Greek",
848 | "link": "https://en.wikipedia.org/wiki/Oedipus_the_King\n",
849 | "pages": 88,
850 | "name": "Oedipus the King",
851 | "article": -430
852 | },
853 | {
854 | "brand": "Stendhal",
855 | "country": "France",
856 | "imageLink": "images/le-rouge-et-le-noir.jpg",
857 | "language": "French",
858 | "link": "https://en.wikipedia.org/wiki/The_Red_and_the_Black\n",
859 | "pages": 576,
860 | "name": "The Red and the Black",
861 | "article": 1830
862 | },
863 | {
864 | "brand": "Laurence Sterne",
865 | "country": "England",
866 | "imageLink": "images/the-life-and-opinions-of-tristram-shandy.jpg",
867 | "language": "English",
868 | "link": "https://en.wikipedia.org/wiki/The_Life_and_Opinions_of_Tristram_Shandy,_Gentleman\n",
869 | "pages": 640,
870 | "name": "The Life And Opinions of Tristram Shandy",
871 | "article": 1760
872 | },
873 | {
874 | "brand": "Italo Svevo",
875 | "country": "Italy",
876 | "imageLink": "images/confessions-of-zeno.jpg",
877 | "language": "Italian",
878 | "link": "https://en.wikipedia.org/wiki/Zeno%27s_Conscience\n",
879 | "pages": 412,
880 | "name": "Confessions of Zeno",
881 | "article": 1923
882 | },
883 | {
884 | "brand": "Jonathan Swift",
885 | "country": "Ireland",
886 | "imageLink": "images/gullivers-travels.jpg",
887 | "language": "English",
888 | "link": "https://en.wikipedia.org/wiki/Gulliver%27s_Travels\n",
889 | "pages": 178,
890 | "name": "Gulliver's Travels",
891 | "article": 1726
892 | },
893 | {
894 | "brand": "Leo Tolstoy",
895 | "country": "Russia",
896 | "imageLink": "images/war-and-peace.jpg",
897 | "language": "Russian",
898 | "link": "https://en.wikipedia.org/wiki/War_and_Peace\n",
899 | "pages": 1296,
900 | "name": "War and Peace",
901 | "article": 1867
902 | },
903 | {
904 | "brand": "Leo Tolstoy",
905 | "country": "Russia",
906 | "imageLink": "images/anna-karenina.jpg",
907 | "language": "Russian",
908 | "link": "https://en.wikipedia.org/wiki/Anna_Karenina\n",
909 | "pages": 864,
910 | "name": "Anna Karenina",
911 | "article": 1877
912 | },
913 | {
914 | "brand": "Leo Tolstoy",
915 | "country": "Russia",
916 | "imageLink": "images/the-death-of-ivan-ilyich.jpg",
917 | "language": "Russian",
918 | "link": "https://en.wikipedia.org/wiki/The_Death_of_Ivan_Ilyich\n",
919 | "pages": 92,
920 | "name": "The Death of Ivan Ilyich",
921 | "article": 1886
922 | },
923 | {
924 | "brand": "Mark Twain",
925 | "country": "United States",
926 | "imageLink": "images/the-adventures-of-huckleberry-finn.jpg",
927 | "language": "English",
928 | "link": "https://en.wikipedia.org/wiki/Adventures_of_Huckleberry_Finn\n",
929 | "pages": 224,
930 | "name": "The Adventures of Huckleberry Finn",
931 | "article": 1884
932 | },
933 | {
934 | "brand": "Valmiki",
935 | "country": "India",
936 | "imageLink": "images/ramayana.jpg",
937 | "language": "Sanskrit",
938 | "link": "https://en.wikipedia.org/wiki/Ramayana\n",
939 | "pages": 152,
940 | "name": "Ramayana",
941 | "article": -450
942 | },
943 | {
944 | "brand": "Virgil",
945 | "country": "Roman Empire",
946 | "imageLink": "images/the-aeneid.jpg",
947 | "language": "Classical Latin",
948 | "link": "https://en.wikipedia.org/wiki/Aeneid\n",
949 | "pages": 442,
950 | "name": "The Aeneid",
951 | "article": -23
952 | },
953 | {
954 | "brand": "Vyasa",
955 | "country": "India",
956 | "imageLink": "images/the-mahab-harata.jpg",
957 | "language": "Sanskrit",
958 | "link": "https://en.wikipedia.org/wiki/Mahabharata\n",
959 | "pages": 276,
960 | "name": "Mahabharata",
961 | "article": -700
962 | },
963 | {
964 | "brand": "Walt Whitman",
965 | "country": "United States",
966 | "imageLink": "images/leaves-of-grass.jpg",
967 | "language": "English",
968 | "link": "https://en.wikipedia.org/wiki/Leaves_of_Grass\n",
969 | "pages": 152,
970 | "name": "Leaves of Grass",
971 | "article": 1855
972 | },
973 | {
974 | "brand": "Virginia Woolf",
975 | "country": "United Kingdom",
976 | "imageLink": "images/mrs-dalloway.jpg",
977 | "language": "English",
978 | "link": "https://en.wikipedia.org/wiki/Mrs_Dalloway\n",
979 | "pages": 216,
980 | "name": "Mrs Dalloway",
981 | "article": 1925
982 | },
983 | {
984 | "brand": "Virginia Woolf",
985 | "country": "United Kingdom",
986 | "imageLink": "images/to-the-lighthouse.jpg",
987 | "language": "English",
988 | "link": "https://en.wikipedia.org/wiki/To_the_Lighthouse\n",
989 | "pages": 209,
990 | "name": "To the Lighthouse",
991 | "article": 1927
992 | },
993 | {
994 | "brand": "Marguerite Yourcenar",
995 | "country": "France/Belgium",
996 | "imageLink": "images/memoirs-of-hadrian.jpg",
997 | "language": "French",
998 | "link": "https://en.wikipedia.org/wiki/Memoirs_of_Hadrian\n",
999 | "pages": 408,
1000 | "name": "Memoirs of Hadrian",
1001 | "article": 1951
1002 | }
1003 | ]
1004 | }
--------------------------------------------------------------------------------