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
11
csvtosql.js
11
csvtosql.js
|
@ -25,7 +25,7 @@ const db = await getClient();
|
||||||
const file = fs.readFileSync("./output.csv").toString();
|
const file = fs.readFileSync("./output.csv").toString();
|
||||||
|
|
||||||
const inFileStream = fs.createReadStream("./output.csv");
|
const inFileStream = fs.createReadStream("./output.csv");
|
||||||
|
await db.query("BEGIN");
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: inFileStream,
|
input: inFileStream,
|
||||||
output: null,
|
output: null,
|
||||||
|
@ -34,9 +34,12 @@ const rl = readline.createInterface({
|
||||||
|
|
||||||
rl.on('line', async (line) => {
|
rl.on('line', async (line) => {
|
||||||
let splitted = line.split(",")
|
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', () => {
|
rl.on('close', async () => {
|
||||||
console.log("done");
|
await db.query("COMMIT");
|
||||||
|
process.exit(0)
|
||||||
|
|
||||||
})
|
})
|
20
scheme.psql
20
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 UNIQUE INDEX IF NOT EXISTS unique_messages ON messages(sender, recipient);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS cities (
|
CREATE TABLE IF NOT EXISTS cities (
|
||||||
name VARCHAR(32),
|
name VARCHAR(64),
|
||||||
lat REAL,
|
lat REAL,
|
||||||
lng 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;
|
96
src/db.js
96
src/db.js
|
@ -61,33 +61,50 @@ const eraseUserMedia = async (roomId) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectProfilesForUser = async (roomId) => {
|
const selectProfilesForUser = async (roomId) => {
|
||||||
let myInterest = (await db.query("SELECT interest FROM users WHERE room_id = $1", [roomId])).rows[0].interest;
|
const { myrange, myinterest, mysex, myage, mycity, mycountry } = (await db.query(`SELECT range AS myrange,
|
||||||
let mySex = (await db.query("SELECT sex FROM users WHERE room_id = $1", [roomId])).rows[0].sex;
|
interest AS myinterest,
|
||||||
let userAge = (await db.query("SELECT age FROM users WHERE room_id = $1", [roomId])).rows[0].age;
|
sex AS mysex,
|
||||||
//Selecting profiles other than user's and with difference in age +-2.
|
age AS myage,
|
||||||
let user;
|
city AS mycity,
|
||||||
if (myInterest === 'b') // both, no matter what sex
|
country AS mycountry
|
||||||
user = (await db.query(`SELECT
|
FROM users WHERE room_id = $1`, [roomId])
|
||||||
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];
|
).rows[0];
|
||||||
else {
|
const { lat, lng } = (await db.query("SELECT lat, lng FROM cities WHERE name = $1 AND country = $2", [mycity, mycountry])).rows[0];
|
||||||
user = (await db.query(`SELECT
|
//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
|
room_id, name, age, sex, description, country, city FROM users
|
||||||
WHERE
|
WHERE
|
||||||
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
|
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
|
||||||
AND room_id != $2
|
AND room_id != $2
|
||||||
AND sex = $3
|
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')
|
AND (interest = $4 OR interest = 'b')
|
||||||
ORDER BY RANDOM()
|
ORDER BY RANDOM()
|
||||||
LIMIT 1`, [userAge, roomId, myInterest, mySex])
|
LIMIT 1`, [myage, roomId, myinterest, mysex, myrange, lat, lng])
|
||||||
).rows[0];
|
).rows[0];
|
||||||
}
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
let media = await getUserProfilePictures(user.room_id);
|
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;
|
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 {
|
export {
|
||||||
eraseUser,
|
eraseUser,
|
||||||
appendUserPictures,
|
appendUserPictures,
|
||||||
|
@ -196,5 +250,7 @@ export {
|
||||||
getUnreadMessages,
|
getUnreadMessages,
|
||||||
markMessageAsRead,
|
markMessageAsRead,
|
||||||
setUserLanguage,
|
setUserLanguage,
|
||||||
getUserLanguage
|
getUserLanguage,
|
||||||
|
checkCountry,
|
||||||
|
checkCity
|
||||||
};
|
};
|
36
src/index.js
36
src/index.js
|
@ -32,7 +32,9 @@ import {
|
||||||
setUserState,
|
setUserState,
|
||||||
uploadMediaAsMessage,
|
uploadMediaAsMessage,
|
||||||
setUserLanguage,
|
setUserLanguage,
|
||||||
getUserLanguage
|
getUserLanguage,
|
||||||
|
checkCountry,
|
||||||
|
checkCity
|
||||||
} from './db.js';
|
} from './db.js';
|
||||||
|
|
||||||
import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js";
|
import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js";
|
||||||
|
@ -84,15 +86,41 @@ client.on("room.message", async (roomId, event) => {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "country":
|
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');
|
await processRequest(client, roomId, current_action, answer, 'city');
|
||||||
break;
|
break;
|
||||||
case "city":
|
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');
|
await processRequest(client, roomId, current_action, answer, 'range');
|
||||||
break;
|
break;
|
||||||
case "range":
|
case "range":
|
||||||
answer = parseInt(answer.split(" ")[0]);
|
answer = parseInt(answer.split(" ")[0]);
|
||||||
console.log(answer)
|
if (!answer && answer != 0) {
|
||||||
if (!answer) {
|
|
||||||
await client.sendText(roomId, i18n.t(["setup", "range"]));
|
await client.sendText(roomId, i18n.t(["setup", "range"]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +215,7 @@ client.on("room.message", async (roomId, event) => {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if (answer == '🏠️' || answer == '4') {
|
} else if (answer == '🏠️' || answer == '4') {
|
||||||
await client.sendText(roomId, messages.general.menu);
|
await client.sendText(roomId, i18n.t(["general", "menu"]));
|
||||||
await setUserState(roomId, 'menu');
|
await setUserState(roomId, 'menu');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,7 @@ ${chosenProfile.description}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await client.sendText(roomId, i18n.t(["general", "rate"]));
|
||||||
await client.sendText(roomId, messages.general.rate);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const showProfileToUser = async (client, roomId, profileId) => {
|
const showProfileToUser = async (client, roomId, profileId) => {
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
"didntunderstand": "I cannot understand your answer. Please, read my question again and answer!",
|
"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! ;)",
|
"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!",
|
"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": "Я не понимаю ваш ответ. Пожалуйста, перечитайте моё прошлое сообщение и попробуйте снова!",
|
"didntunderstand": "Я не понимаю ваш ответ. Пожалуйста, перечитайте моё прошлое сообщение и попробуйте снова!",
|
||||||
"notimplemented": "Эта возможность ещё не добавлена! Пожалуйста, следите за обновлениями на моём git'е: https://git.foxarmy.org/leca/heart2heart. Доставайте своих админов, чтобы обновлялись, когда новая версия выходит ;)",
|
"notimplemented": "Эта возможность ещё не добавлена! Пожалуйста, следите за обновлениями на моём git'е: https://git.foxarmy.org/leca/heart2heart. Доставайте своих админов, чтобы обновлялись, когда новая версия выходит ;)",
|
||||||
"noprofiles": "Не могу найти профили, которые подходят вашим критериям! Извините. Попробуйте позже!",
|
"noprofiles": "Не могу найти профили, которые подходят вашим критериям! Извините. Попробуйте позже!",
|
||||||
"alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!"
|
"alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!",
|
||||||
|
"wrongcountry": "Не знаю такой страны. Возможно, вы имели в виду %{first}, %{second} или %{third}?",
|
||||||
|
"wrongcity": "Не знаю такого города. Возможно, вы имели в виду %{first}, %{second}, %{third}, %{fourth} или %{fifth}?"
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue