Я пытаюсь импортировать файл csv
в neo4j
с помощью neo4j
Мне нужно вставить данные в несколько collection/table
, поэтому мне нужно вставить данные, используя script.js. Но моя проблема заключается в том, что я не могу предотвратить дублирование данных при вставке данных csv.
Примеры данных CSV:
name
-------------
Afghanistan
Afghanistan
Aland
Albania
Albania
Bangladesh
Bangladesh
index.js
cp = require('child_process');
child = cp.fork(__dirname + "/background-import-csv-file.js");
child.on('message', function(msg) {
console.log("background-insert-process said : ", msg);
});
file = path.resolve(__dirname, './file/simplemaps.csv');
child.send(file);
В background-import-csv-file.js
меня есть код записи двумя разными способами.
Первое основанное на обещании (background-import-csv-file.js
):
cp = require('child_process');
csv = require('fast-csv');
Q = require('q');
DB = require("./common/driver");
Country = require('./collection/country');
process.on("message", (file) => {
stream = fs.createReadStream(file);
csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
let countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.insert(countryData)
.then(resp => process.send(resp.msg) )
.catch(err => process.send(err) )
})
.on("end", () => process.send("file read complete") );
});
./collection/country.js
:
Q = require('q');
Country = function Country(neo) {
this.country = "Country"; this.neo = neo;
};
Country.prototype.find = function find(filters) {
query = 'MATCH (a:Country { name: '${filters.name}' } ) RETURN {country:properties(a)}';
return this.neo.run(query, filters).then(resp => resp);
}
Country.prototype.create = function create(data) {
query = 'CREATE (ax:Country { name: '${data.name}' } ) RETURN ax ';
return this.neo.run(query, {}).then(resp => resp[0].properties).catch(err => err)
}
Country.prototype.insert = function insert(country) {
filter = { name: country.name };
return Q(this.find(filter))
.then(resp => resp.length > 0 ? Q.resolve({ msg: 'country: [${country.name}] is already exist' }) : Q.resolve(this.create(country)) )
.then(resp => resp)
.catch(e => Q.reject(e));
}
module.exports = Country;
./common/driver.js
neo4j = require('neo4j-driver').v1;
function DB() {
this.driver = neo4j.driver(); this.session = this.driver.session();
}
DB.prototype.run = function run(query, data) {
return this.session.run(query, data)
.then(response => response.records.map(
record => record._fields[0] ?
record._fields.length ? record._fields[0] : {} : {}
) ).catch(err => new Error(err) );
}
module.exports = DB;
Когда я запускаю index.js
в терминале, в базе данных у меня есть 2 Afghanistan
, 1 Aland
, 2 Albania
и 2 Bangladesh
. Но мне нужна 1 Afghanistan
, 1 Aland
, 1 Albania
и 1 Bangladesh
в моей базе данных. Когда я анализирую код, чем обнаружил, что перед вставкой данных я проверяю данные (Country.prototype.find = function find(filters)
), если он уже существует или нет, но он всегда возвращает пустой результат. Вот почему он вставляет несколько данных. Если я снова запустил index.js
, то новые данные не будут добавлены в базу данных. Чтобы решить эту проблему, я попытался CQL
:
MERGE (c:Country { name: '${data.name}' } ) RETURN c
В него вставляются уникальные данные, но он убивает так много времени. Затем я написал следующий код:
Событие (background-import-csv-file.js
):
process.on("message", (file) => {
stream = fs.createReadStream(file);
csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.find(countryData);
country.on('find', resp => resp.length > 0 ? Q.resolve({ msg: 'country: [${country.name}] is already exist' }) : Q.resolve(country.create(countryData)) );
country.on('create', resp => console.log(resp) );
})
.on("end", () => process.send("file read complete") );
});
./collection/country.js
:
EventEmitter = require('events').EventEmitter;
util = require('util');
Country = function Country(neo) {
this.neo = neo; EventEmitter.call(this);
};
util.inherits(Country, EventEmitter);
Country.prototype.find = function find(filters) {
query = 'MATCH (a:Country { name: '${filters.name}' } ) RETURN {country:properties(a)}';
return this.neo.run(query, {}).then(resp => this.emit('find', resp));
}
Country.prototype.create = function create(data) {
query = 'CREATE (ax:Country { name: '${data.name}' } ) RETURN ax ';
return this.neo.run(query, {}).then(resp => this.emit('create', resp[0].properties)).catch(err => err)
}
И на этот раз он показывает тот же результат. Что мне не хватает? Любое предложение будет очень полезно.
NB: Я использую fast-csv
для синтаксического анализа csv и Q
для обещания.
Моя проблема заключалась в том, что в синтаксическом анализе csv
он был настолько быстрым (управляемым событиями), что не дождался завершения вставки данных в базу данных. Поэтому я должен приостановить синтаксический анализ файлов, а затем возобновить его.
Я решаю свою проблему, используя следующий код:
На основе обещаний (background-import-csv-file.js):
cp = require('child_process');
csv = require('fast-csv');
Q = require('q');
DB = require("./common/driver");
Country = require('./collection/country');
process.on("message", (file) => {
stream = fs.createReadStream(file);
csvstream = csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
csvstream.pause(); // pause the csv file parsing
countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.insert(countryData)
.then(resp => {
process.send(resp.msg);
neo.close();
return csvstream.resume(); // after completing db process, resume
})
.catch(err => {
process.send(err);
return csvstream.resume(); // if failed, then resume
});
})
.on("end", () => process.send("file read complete") );
});
На самом деле я могу представить себе следующие решения:
CREATE CONSTRAINT ON (c:Country) ASSERT c.name IS UNIQUE
выше.