Skip to content
This repository has been archived by the owner on Oct 18, 2021. It is now read-only.

how to work with update one #294

Closed
karasjoh000 opened this issue Nov 24, 2018 · 13 comments
Closed

how to work with update one #294

karasjoh000 opened this issue Nov 24, 2018 · 13 comments

Comments

@karasjoh000
Copy link

Hello, I have three fields that either all, some or one of them can be updated. Idk know how to set it up in order to take this into account. Attached is what I have so far, but this does not work because need to have those $set keywords in there somewhere somehow.

update_one

@thedodd
Copy link
Contributor

thedodd commented Nov 25, 2018

@karasjoh000 you just need to insert a new document into your update document under the ”$set” key. Then all of the fields which need to be conditionally updated should be put inside of the $set doc.

IMHO, it would be easiest to construct the $set doc first, and then once everything has been added to it, create the parent doc. That would allow you to build the update doc using the doc! macro like so:

// setdoc should already include all of the conditional
// fields, per your example above. 
let update = doc!{“$set”: setdoc};

Then pass that update doc to the update method.

@karasjoh000
Copy link
Author

karasjoh000 commented Nov 25, 2018

When I do that, the other user is not updated a new one is created:
update_one_dup
newuser
I tried updating admin and instead a new user was created. I thought upsert is off by defualt? even then the admin user did exist
new_code

@karasjoh000
Copy link
Author

Ah ha! when inserting the hash, need to use Bson::String(hash) instead of just hash

@karasjoh000
Copy link
Author

Working now, but that was weird the fact that it creates a new user when hash is not in the enum, is this a bug? should it spit out an error instead?

@thedodd
Copy link
Contributor

thedodd commented Nov 26, 2018

Sounds like an algorithmic issue. Please copy and paste the code as a formatted Rust code block instead of an image. The images are a bit hard to follow.

Also, if you don’t mind, perhaps you can include an overview of what the algorithm is supposed to be, that will make it easier to diagnose.

Sound good?

@karasjoh000
Copy link
Author

karasjoh000 commented Nov 26, 2018

Here is the api endpoint that will call the update function upon request.

#[derive(Deserialize)]
pub struct UpdateUser {
    email: String,
    update: UpdateUserData,
}

#[derive(Deserialize)]
pub struct UpdateUserData {
    email: Option<String>,
    password: Option<String>,
    user_type: Option<i32>,
}

#[put(
    "/users/update",
    format = "application/json",
    data = "<jupdate>",
    rank = 3
)]
pub fn put_admin_user_update(
    jupdate: Json<UpdateUser>,
    admin: guards::Admin,
    conn: db::Conn,
) -> Json<Value> {
    let update = jupdate.into_inner();

    if let Some(user_type) = db::users::get_user_type(update.email.clone(), &conn) {
        if user_type == 1 {
            if let Some(new_user_type) = update.update.user_type {
                return Json(
                    json!({"status": "fail", "description": "cannot update user_type for admin"}),
                );
            }
        }
    }

    if let Some(user_type) = update.update.user_type {
        if user_type == 1 {
            return Json(
                json!({"status": "fail", "description": "cannot make more than one admin"}),
            );
        }
    }

    match db::users::update_user(
        update.email,
        update.update.email,
        update.update.password,
        update.update.user_type,
        &conn,
    ) {
        Ok(res) => Json(json!({"status": "ok"})),
        Err(mongodb::Error::DefaultError(s)) => Json(json!({"status": "fail", "description": s})),
        Err(e) => Json(json!({"status": "fail", "description": "mongo error"})),
    }
}

Here is the database function that will attempt to update user info. The problem is when the bson::Bson::String(hash) is replaced with just hash instead.

pub fn update_user(
    email: String,
    new_email: Option<String>,
    new_password: Option<String>,
    new_user_type: Option<i32>,
    db: &Database,
) -> Result<(), mongodb::Error> {

    let mut search = doc!{"email": email};

    match search_doc(search.clone(), &db) {
        Ok(b) => {
            if !b {
                return Err(mongodb::Error::DefaultError("user not found".to_string()));
            }
        }
        Err(e) => return Err(e),
    }

    if new_email == None && new_password == None && new_user_type == None {
        return Err(mongodb::Error::DefaultError(
            "no update fields supplied".to_string(),
        ));
    }

    let mut update = Document::new();

    if let Some(em) = new_email {
        update.insert("email", bson::Bson::String(em));
    }

    if let Some(ps) = new_password {
        let hash: String = scrypt_simple(&ps, &ScryptParams::new(14, 8, 1)).expect("hash error");
        update.insert("hash", bson::Bson::String(hash));
    }

    if let Some(ut) = new_user_type {
        update.insert("user_type", bson::Bson::I32(ut));
    }

    match db.collection("users").update_one(search, doc!{"$set": update}, None) {
        Ok(res) => {
            println!("{:?}", res);
            Ok(())
        },
        Err(e) => {
            println!("{}", e);
            println!("update failed");
            Err(e)
        }
    }
}

In the put_admin_user_update function it checks some permissions what the user is trying to attempt and if he is allowed to do that then it calls the database function update_user to update the user. The database function searches if user exists then updates only if user exists. Here is the function search_doc that searches if the user already exists.

fn search_doc(doc: Document, db: &Database) -> Result<bool, mongodb::Error> {
    match db.collection("users").find_one(Some(doc.clone()), None) {
        Ok(res) => match res {
            Some(doc) => Ok(true),
            None => Ok(false),
        },
        Err(t) => {
            println!("search failed");
            Err(t)
        }
    }
}

I will try to investigate this problem a little more once my finals finish next month, might be some weird thing that happened somewhere in the code.

@codenoid
Copy link

codenoid commented Dec 3, 2018

idk, but thanks for bson::Bson::I32() syntax

@karasjoh000
Copy link
Author

lol no problem. Here is the full list of the different types: https://docs.rs/bson/0.6.0/bson/enum.Bson.html

@thedodd
Copy link
Contributor

thedodd commented Dec 4, 2018

Hey @karasjoh000, so according to the code snippets above, have you resolved the original issue and now you are having a problem with the hash value being unexpected?

It looks like the issue you originally opened this ticket for is resolved and you are no longer having an issue with using $set and the code seems to be in order. Is my analysis correct?

@karasjoh000
Copy link
Author

karasjoh000 commented Dec 4, 2018

Yes the initial problem is solved. You can close it if you think its appropriate but there is a wierd bug if str is just provided instead of Bson::String(str)

@thedodd
Copy link
Contributor

thedodd commented Dec 4, 2018

Cool. Glad to hear the first issue is resolved. As far as the string issue ... maybe you should close this issue and open a new one for the string issue specifically. That would def be easier to track. What exactly is the problem that you are seeing (would be good to add that info to the new issue)?

Also, check out the wither project for modeling your data and such. It’s an ODM. It may help with some aspects of managing your data.

@thedodd
Copy link
Contributor

thedodd commented Jan 14, 2019

@karasjoh000 hey. Just wanted to follow up with you and see if everything has been working out. If so, mind closing this issue?

@jcdyer
Copy link

jcdyer commented Sep 28, 2019

@saghm @kyeah this issue can also be closed, per the above conversation.

@saghm saghm closed this as completed Sep 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants