Complete location as a criteria for search
This commit is contained in:
parent
7220c6d2c2
commit
971d263d35
43119
cities.sql
43119
cities.sql
File diff suppressed because it is too large
Load Diff
13
csvtosql.js
13
csvtosql.js
|
@ -25,18 +25,21 @@ const db = await getClient();
|
|||
const file = fs.readFileSync("./output.csv").toString();
|
||||
|
||||
const inFileStream = fs.createReadStream("./output.csv");
|
||||
|
||||
await db.query("BEGIN");
|
||||
const rl = readline.createInterface({
|
||||
input: inFileStream,
|
||||
output: null,
|
||||
output: null,
|
||||
crlfDelay: Infinity
|
||||
})
|
||||
|
||||
rl.on('line', async (line) => {
|
||||
let splitted = line.split(",")
|
||||
await db.query(`INSERT INTO cities (name, lat, lng, country) VALUES ($1, $2, $3, $4);`, [splitted[0], splitted[1], splitted[2], splitted[3]])
|
||||
console.log(splitted);
|
||||
await db.query(`INSERT INTO cities (name, lat, lng, country) VALUES ($1, $2, $3, $4)`, [splitted[0], splitted[1], splitted[2], splitted[3]])
|
||||
});
|
||||
|
||||
rl.on('close', () => {
|
||||
console.log("done");
|
||||
rl.on('close', async () => {
|
||||
await db.query("COMMIT");
|
||||
process.exit(0)
|
||||
|
||||
})
|
22
scheme.psql
22
scheme.psql
|
@ -40,8 +40,24 @@ CREATE TABLE IF NOT EXISTS messages (
|
|||
CREATE UNIQUE INDEX IF NOT EXISTS unique_messages ON messages(sender, recipient);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cities (
|
||||
name VARCHAR(32),
|
||||
name VARCHAR(64),
|
||||
lat REAL,
|
||||
lng REAL,
|
||||
country VARCHAR(32)
|
||||
);
|
||||
country VARCHAR(64)
|
||||
);
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch SCHEMA public;
|
||||
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION deg2rad(double precision) RETURNS double precision AS 'SELECT $1 * 0.01745329' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;
|
||||
-- lat1, lng1, lat2, lng2, range
|
||||
CREATE OR REPLACE FUNCTION check_distance(double precision, double precision, double precision, double precision, double precision) RETURNS BOOLEAN AS '
|
||||
SELECT 2 * ASIN(SQRT(
|
||||
POWER(SIN( (deg2rad($3 - $1)) / 2 ), 2)
|
||||
+ COS(deg2rad($1))
|
||||
* COS(deg2rad($3))
|
||||
* POWER(SIN(deg2rad($4 - $2)/2), 2)
|
||||
))
|
||||
* 6371 <= $5
|
||||
' LANGUAGE SQL RETURNS NULL ON NULL INPUT;
|
114
src/db.js
114
src/db.js
|
@ -19,7 +19,7 @@ const getClient = async () => {
|
|||
|
||||
if ((await client.query("SELECT * FROM cities")).rowCount < 6079) {
|
||||
await client.query("DELETE FROM cities");
|
||||
|
||||
|
||||
//Not sure if pg has support for such kind of things, sooooooooo
|
||||
exec(`psql -h ${process.env.POSTGRES_HOST} -p ${process.env.POSTGRES_PORT} -d ${process.env.POSTGRES_DB} -U ${process.env.POSTGRES_USER} -f ./cities.sql`, (error) => {
|
||||
if (error) logError(error);
|
||||
|
@ -61,33 +61,50 @@ const eraseUserMedia = async (roomId) => {
|
|||
};
|
||||
|
||||
const selectProfilesForUser = async (roomId) => {
|
||||
let myInterest = (await db.query("SELECT interest FROM users WHERE room_id = $1", [roomId])).rows[0].interest;
|
||||
let mySex = (await db.query("SELECT sex FROM users WHERE room_id = $1", [roomId])).rows[0].sex;
|
||||
let userAge = (await db.query("SELECT age FROM users WHERE room_id = $1", [roomId])).rows[0].age;
|
||||
//Selecting profiles other than user's and with difference in age +-2.
|
||||
let user;
|
||||
if (myInterest === 'b') // both, no matter what sex
|
||||
user = (await db.query(`SELECT
|
||||
room_id, name, age, sex, description, country, city FROM users
|
||||
WHERE
|
||||
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
|
||||
AND room_id != $2
|
||||
AND (interest = $3 OR interest = 'b')
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 1`, [userAge, roomId, mySex])
|
||||
).rows[0];
|
||||
else {
|
||||
user = (await db.query(`SELECT
|
||||
room_id, name, age, sex, description, country, city FROM users
|
||||
WHERE
|
||||
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
|
||||
AND room_id != $2
|
||||
AND sex = $3
|
||||
AND (interest = $4 OR interest = 'b')
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 1`, [userAge, roomId, myInterest, mySex])
|
||||
).rows[0];
|
||||
}
|
||||
const { myrange, myinterest, mysex, myage, mycity, mycountry } = (await db.query(`SELECT range AS myrange,
|
||||
interest AS myinterest,
|
||||
sex AS mysex,
|
||||
age AS myage,
|
||||
city AS mycity,
|
||||
country AS mycountry
|
||||
FROM users WHERE room_id = $1`, [roomId])
|
||||
).rows[0];
|
||||
const { lat, lng } = (await db.query("SELECT lat, lng FROM cities WHERE name = $1 AND country = $2", [mycity, mycountry])).rows[0];
|
||||
//Selecting profiles other than user's and fitting their needs
|
||||
/*
|
||||
2 * ASIN(SQRT(
|
||||
POWER(SIN( (deg2rad(lat - $6::double precision)) / 2 ), 2)
|
||||
+ COS(deg2rad(lat))
|
||||
* COS(deg2rad($6::double precision))
|
||||
* POWER(SIN(deg2rad(lng - $7::double precision)/2), 2)
|
||||
))
|
||||
* 6371 <= $5::double precision
|
||||
*/
|
||||
let user = (await db.query(`SELECT
|
||||
room_id, name, age, sex, description, country, city FROM users
|
||||
WHERE
|
||||
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
|
||||
AND room_id != $2
|
||||
AND ${myinterest !== 'b' ? "sex = $3" : "$3 = $3 AND $4 = $4 AND $5 = $5 AND $6 = $6 AND $7 = $7"}
|
||||
AND ${myrange !== 0 ?
|
||||
`city = ANY(ARRAY(SELECT name
|
||||
FROM cities
|
||||
WHERE name = ANY(ARRAY(SELECT name
|
||||
FROM cities
|
||||
WHERE
|
||||
check_distance($6::double precision, $7::double precision, lat, lng, $5::double precision)
|
||||
|
||||
))
|
||||
))`
|
||||
:
|
||||
`check_distance($6::double precision, $7::double precision, (SELECT lat FROM cities WHERE name = city AND cities.country = country), (SELECT lng FROM cities WHERE name = city AND cities.country = country), range)`
|
||||
}
|
||||
AND (range >= $5::double precision AND range != 0)
|
||||
AND (interest = $4 OR interest = 'b')
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 1`, [myage, roomId, myinterest, mysex, myrange, lat, lng])
|
||||
).rows[0];
|
||||
|
||||
if (!user) return null;
|
||||
let media = await getUserProfilePictures(user.room_id);
|
||||
|
||||
|
@ -174,6 +191,43 @@ const getUserLanguage = async (roomId) => {
|
|||
return (await db.query("SELECT language FROM users WHERE room_id = $1", [roomId])).rows[0].language;
|
||||
}
|
||||
|
||||
const checkCountry = async (name) => {
|
||||
let res = (await db.query(`SELECT country AS name, levenshtein(country, $1) AS similarity
|
||||
FROM cities
|
||||
ORDER BY similarity ASC
|
||||
LIMIT 3`, [name])
|
||||
).rows;
|
||||
|
||||
if (res[0].similarity == 0) { // 'similarity' is actually inversed. The less 'similarity', the more it similar. 0 means the same
|
||||
return {
|
||||
exists: true
|
||||
};
|
||||
}
|
||||
return {
|
||||
exists: false,
|
||||
variants: res
|
||||
};
|
||||
}
|
||||
|
||||
const checkCity = async (name) => {
|
||||
let res = (await db.query(`SELECT name, country, levenshtein(name, $1) AS similarity
|
||||
FROM cities
|
||||
ORDER BY similarity ASC
|
||||
LIMIT 5`, [name])
|
||||
).rows;
|
||||
|
||||
if (res[0].similarity == 0) {
|
||||
return {
|
||||
exists: true,
|
||||
country: res[0].country
|
||||
}
|
||||
}
|
||||
return {
|
||||
exists: false,
|
||||
variants: res
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
eraseUser,
|
||||
appendUserPictures,
|
||||
|
@ -196,5 +250,7 @@ export {
|
|||
getUnreadMessages,
|
||||
markMessageAsRead,
|
||||
setUserLanguage,
|
||||
getUserLanguage
|
||||
getUserLanguage,
|
||||
checkCountry,
|
||||
checkCity
|
||||
};
|
36
src/index.js
36
src/index.js
|
@ -32,7 +32,9 @@ import {
|
|||
setUserState,
|
||||
uploadMediaAsMessage,
|
||||
setUserLanguage,
|
||||
getUserLanguage
|
||||
getUserLanguage,
|
||||
checkCountry,
|
||||
checkCity
|
||||
} from './db.js';
|
||||
|
||||
import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js";
|
||||
|
@ -84,15 +86,41 @@ client.on("room.message", async (roomId, event) => {
|
|||
|
||||
break;
|
||||
case "country":
|
||||
let checkResultCountry = await checkCountry(answer);
|
||||
if (!checkResultCountry.exists) {
|
||||
console.log(checkResultCountry.variants)
|
||||
await client.sendText(roomId, i18n.t(
|
||||
["errors", "wrongcountry"],
|
||||
{
|
||||
first: checkResultCountry.variants[0].name,
|
||||
second: checkResultCountry.variants[1].name,
|
||||
third: checkResultCountry.variants[2].name
|
||||
}
|
||||
));
|
||||
return;
|
||||
}
|
||||
await processRequest(client, roomId, current_action, answer, 'city');
|
||||
break;
|
||||
case "city":
|
||||
let checkResultCity = await checkCity(answer);
|
||||
if (!checkResultCity.exists) {
|
||||
await client.sendText(roomId, i18n.t(
|
||||
["errors", "wrongcity"],
|
||||
{
|
||||
first: checkResultCity.variants[0].name,
|
||||
second: checkResultCity.variants[1].name,
|
||||
third: checkResultCity.variants[2].name,
|
||||
fourth: checkResultCity.variants[3].name,
|
||||
fifth: checkResultCity.variants[4].name,
|
||||
}
|
||||
));
|
||||
return;
|
||||
}
|
||||
await processRequest(client, roomId, current_action, answer, 'range');
|
||||
break;
|
||||
case "range":
|
||||
answer = parseInt(answer.split(" ")[0]);
|
||||
console.log(answer)
|
||||
if (!answer) {
|
||||
if (!answer && answer != 0) {
|
||||
await client.sendText(roomId, i18n.t(["setup", "range"]));
|
||||
return;
|
||||
}
|
||||
|
@ -187,7 +215,7 @@ client.on("room.message", async (roomId, event) => {
|
|||
|
||||
return;
|
||||
} else if (answer == '🏠️' || answer == '4') {
|
||||
await client.sendText(roomId, messages.general.menu);
|
||||
await client.sendText(roomId, i18n.t(["general", "menu"]));
|
||||
await setUserState(roomId, 'menu');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -87,8 +87,7 @@ ${chosenProfile.description}`;
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
await client.sendText(roomId, messages.general.rate);
|
||||
await client.sendText(roomId, i18n.t(["general", "rate"]));
|
||||
};
|
||||
|
||||
const showProfileToUser = async (client, roomId, profileId) => {
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
"didntunderstand": "I cannot understand your answer. Please, read my question again and answer!",
|
||||
"notimplemented": "This feature is not yet implemented! Please, keep track of my updates at https://git.foxarmy.org/leca/heart2heart. Bother your admins to update, if released! ;)",
|
||||
"noprofiles": "There are no profiles left for you! I'm sorry. Please, come back later!",
|
||||
"alreadymessaged": "You have already messaged that person. Your message was not sent!"
|
||||
"alreadymessaged": "You have already messaged that person. Your message was not sent!",
|
||||
"wrongcountry": "There is no such country. Perhaps, you meant %{first}, %{second} or %{third}?",
|
||||
"wrongcity": "There is no such city. Perhaps, you meant %{first}, %{second}, %{third}, %{fourth} or %{fifth}?"
|
||||
}
|
||||
}
|
|
@ -37,7 +37,8 @@
|
|||
"didntunderstand": "Я не понимаю ваш ответ. Пожалуйста, перечитайте моё прошлое сообщение и попробуйте снова!",
|
||||
"notimplemented": "Эта возможность ещё не добавлена! Пожалуйста, следите за обновлениями на моём git'е: https://git.foxarmy.org/leca/heart2heart. Доставайте своих админов, чтобы обновлялись, когда новая версия выходит ;)",
|
||||
"noprofiles": "Не могу найти профили, которые подходят вашим критериям! Извините. Попробуйте позже!",
|
||||
"alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!"
|
||||
|
||||
"alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!",
|
||||
"wrongcountry": "Не знаю такой страны. Возможно, вы имели в виду %{first}, %{second} или %{third}?",
|
||||
"wrongcity": "Не знаю такого города. Возможно, вы имели в виду %{first}, %{second}, %{third}, %{fourth} или %{fifth}?"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue