Complete location as a criteria for search
This commit is contained in:
		
							
								
								
									
										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 inFileStream = fs.createReadStream("./output.csv");
 | 
			
		||||
 | 
			
		||||
await db.query("BEGIN");
 | 
			
		||||
const rl = readline.createInterface({
 | 
			
		||||
    input: inFileStream,
 | 
			
		||||
    output: null,
 | 
			
		||||
@@ -34,9 +34,12 @@ const rl = readline.createInterface({
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										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 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;
 | 
			
		||||
							
								
								
									
										96
									
								
								src/db.js
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/db.js
									
									
									
									
									
								
							@@ -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])
 | 
			
		||||
    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];
 | 
			
		||||
    else {
 | 
			
		||||
        user = (await db.query(`SELECT 
 | 
			
		||||
    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 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')
 | 
			
		||||
                            ORDER BY RANDOM()
 | 
			
		||||
                                LIMIT 1`, [userAge, roomId, myInterest, mySex])
 | 
			
		||||
                            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}?"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user