How do I remove an array item in Mongodb / Golang? - mongodb

I have the following data structure, and I'm attempting to remove an item from the 'artists' array.
[
{
"id": "56b26eeb4a876400011369e9",
"name": "Ewan Valentine",
"email": "ewan#test.com",
"artists": [
"56b26f334a876400011369ea",
"56b2702881318d0001dd1441",
"56b2746fdf1d7e0001faaa92",
],
"user_location": "Manchester, UK"
}
]
Here's my function...
// Remove artist from user
func (repo *UserRepo) RemoveArtist(userId string, artistId string) error {
change := bson.M{"artists": bson.M{"$pull": bson.ObjectIdHex(artistId)}}
fmt.Println(userId)
err := repo.collection.UpdateId(bson.ObjectIdHex(userId), change)
return err
}
I'm getting the following error:
{
"_message": {
"Err": "The dollar ($) prefixed field '$pull' in 'artists.$pull' is not valid for storage.",
"Code": 52,
"N": 0,
"Waited": 0,
"FSyncFiles": 0,
"WTimeout": false,
"UpdatedExisting": false,
"UpsertedId": null
}
}

The $pull operator is a "top level" operator in update statements, so you simply have this the wrong way around:
change := bson.M{"$pull": bson.M{"artists": bson.ObjectIdHex(artistId)}}
The order of update operators is always operator first, action second.
If there is no operator at the "top level" keys, MongoDB interprets this as just a "plain object" to update and "replace" the matched document. Hence the error about the $ in the key name.

Related

Postgres query array elements inside JSON?

I have Postgres 11 table called fb_designs that has a json column with data structured like so:
{
"listings": [
{
"id": "KTyneMdrAhAEKyC9Aylf",
"active": true
},
{
"id": "ZcjK9M4tuwhWWdK8WcfX",
"active": false
}
]
}
and a tags column in a character varying[] format as so {dWLaRWChaThFPH6b3BpA,BrYiPaUiou020hsmRugR}. Both lengths are undefined.
What I am trying to do is produce a some queries that will let me say in laymans terms,
show me all results where at all items.listings has an
active status and tags contains BrYiPaUiou020hsmRugR
I got this far, however I'm not sure how to add in WHERE uid = "foo", WHERE tags contains "foo", "bar and WHERE title is like %hoot%
SELECT id, title, tags, selected_preview_image, items
FROM fb_designs r, json_array_elements(r.items#>'{listings}') obj
WHERE obj->>'active' = 'true'
GROUP BY id
If they are all true, then none of them are false. Sounds like you want to negate the containment operation over false.
select * from fb_designs where
not items::jsonb #> '{"listings":[{"active": false}]}'
and tags && ARRAY['BrYiPaUiou020hsmRugR']::varchar[]

Query variables in Dgraph filter

I am trying to use a variables (which is a scalar) in a #filter(ge(...)) call, but I run into an error
Given the following query
{
ua(func: uid(0xfb7f7)) {
uid
start_ua {
sua as index
}
recorded_in {
actions #filter(ge(index, sua)){
index
}
}
}
}
I get the following error
{
"errors": [
{
"code": "ErrorInvalidRequest",
"message": "Some variables are defined but not used\nDefined:[sua]\nUsed:[]\n"
}
],
"data": null
}
Now if I remove the sua as ... and the #filter(...) from the query, all works fine.
My Dgraph version is v1.0.13.
I tried replacing #filter(ge(index, sua)) with #filter(ge(index, val(sua))) but I still run into an error:
{
"errors": [
{
"code": "ErrorInvalidRequest",
"message": ": No value found for value variable \"sua\""
}
],
"data": null
}
What am I doing wrong?
Here's what the Dgraph docs say about value variables (emphasis added): https://docs.dgraph.io/query-language/#value-variables
Value variables store scalar values. Value variables are a map from the UIDs
of the enclosing block to the corresponding values.
It therefore only makes sense to use the values from a value variable in a
context that matches the same UIDs - if used in a block matching different
UIDs the value variable is undefined.
The start_ua and recorded_in are different subgraphs, which means variables defined in one are undefined in the other within the same query block.
What you can do is use multiple query blocks. Variables can be accessed across blocks:
{
block1(func: uid(0xfb7f7)) {
uid
start_ua (first: 1) {
sua as index
}
}
block2(func: uid(0xfb7f7)) {
recorded_in {
actions #filter(ge(index, val(sua))) {
index
}
}
}
}
I also added (first: 1) to the start_ua predicate, so that at most 1 node is fetched and stored the sua variable. If your data is already structured that way, then that's not needed.
val(sua) gets the value of the variable sua.

How to do multi-level push of array element in mgo?

So I want to do a multi level push for the struct below :
type Inspector_Pool struct{
Unique_Id string `json:"unique_id" form:"unique_id" query:"unique_id"`
Email string `json:"email" form:"email" query:"email"`
Branch []string `json:"branch" query:"branch"`
Date []Date `json:"date" query:"date"`
}
type Date struct {
Event_Timestamp string `json:"event_timestamp" query:"event_timestamp"`
Event []Event `json:"event" query:"event"`
}
type Event struct {
Event_Name string `json:"event_name" form:"event_name" query:"event_name"`
Event_Id string `json:"event_id" form:"event_id" query:"event_id"`
Status string `json:"status" query:"status"`
}
So what i want is to push data in Event array so what I have to achieve code below:
//push a new node in date array
query := bson.M{"branch":"400612", "unique_id":c.Unique_Id}
update := bson.M{"$push": bson.M{"date": bson.M{"event_timestamp": t, "event": []models.Event{
{
Event:models.INSPECTION,
Event_Id:"123456789",
Status:models.PENDING,
},
}}}}
err = look_up.Update(query, update)
if err != nil {
panic(err)
}
//push the data in the nested event
pushQuery := bson.M{"date.event": bson.M{"event": []models.Event{
{
Event_Name:models.INSPECTION,
Event_Id:"4984984198",
Status:models.PENDING,
},
}}}
err = look_up.Update(bson.M{"unique_id": "2549090", "date.event_timestamp":"08-05-2017"}, bson.M{"$push": pushQuery})
if err != nil {
//panic(err)
fmt.Print("error_2",err)
}
but it doesn'tpush it in the event object but instead create a new entry in date object heres the snapshot
You can utilise the $ positional operator to update. Which basically identifies an element in an array to update without explicitly specifying the position of the element in the array.
Alter your second push statement as below:
pushQuery := bson.M{"date.$.event": Event{
Event_Name:"foobar",
Event_Id:"4984984198",
}}
err = collection.Update(bson.M{"unique_id":"2549090",
"date.event_timestamp":"08-05-2017"},
bson.M{"$push": pushQuery})
The above will push event 'foobar' into the same date array matching event_timestamp '08-05-2017' i.e.
{"_id": ObjectId("5909287934cb838fe8f89b6e"),
"unique_id": "2549090",
"date": [
{
"event_timestamp": "08-05-2017",
"event": [
{
"event_name": "baz",
"event_id": "123456789"
},
{
"event_name": "foobar",
"event_id": "4984984198"
}
]
}
]}
Generally having an array of array makes querying or extracting data difficult/complex later on. Depending on your application's use case, I would also suggest to re-consider your document Data Model.

What is wrong with this mongo $or query

This query works perfectly
{
$or:[{author:this.userId} , {somethingelse:true} ]
}
But when I try:
{
$or:[{author:this.userId} , {sharedwith[this.userId]:true} ]
}
I receive the message
Errors prevented startup:
While processing files with ecmascript (for target os.linux.x86_64): server/main.js:113:43: Unexpected token, expected
, (113:43)
=> Your application has errors. Waiting for file change.
And thats where the comma , in the $or statement is
Help
I guess that you are trying to retrieve all documents for which the current user is the author, or which have been shared with him/her? And therefore that you have structured your documents with a sharedWith field which is a hash map of userId as keys and boolean as value?
Document structure:
{
author: string,
sharedWith: {
<userId1>: boolean
// <userId2>…
}
}
In that case, your MongoDB query should use the dot notation to specify the value of a nested field within sharedWith field:
{
$or: [{
author: string
}, {
"sharedWith.<userId>": boolean
}]
}
To easily create the query with the interpolated value of <userId>, you can use a computed key in your object (ES6 syntax):
{
$or:[{
author: this.userId
} , {
// The query computed key must be in square brackets.
// Specify the field child key using the dot notation within your query.
["sharedwith." + this.userId]: true
}]
}
Or with good old ES5 syntax, similarly to #MichelFloyd's answer:
var query = {
$or: [{
author: this.userId
}]
};
var newCondition = {};
newCondition["sharedWith." + this.userId] = true;
query.$or.push(newCondition);
Note: the above described document structure could conveniently replace the sharedWith hash map by an array (since having a false value for the boolean could simply be replaced by removing the corresponding userId from the array):
{
author: string,
sharedWith: [
<userId1>
// <userId2>…
]
}
In which case the query would simply become:
{
$or:[{
author: this.userId
} , {
// In MongoDB query, the below selector matches either:
// - documents where `sharedWith` value is a string equal to
// the value of `this.userId`, or
// - documents where `sharedWith` value is an array which contains
// an element with the same value as `this.userId`.
sharedwith: this.userId
}]
}
Try building the query as a variable before running it.
let query = { $or: [{ author: this.userId }]};
const sw = sharedwith[this.userId];
query.$or.push({sw: true});
MyCollection.find(query);

select function does't work to only get the array i want

i have a document like this
{
"_id": {
"$oid": "570bc73da8ebd9005dd54de3"
},
"title": "dota",
"imgurl": "asd.com",
"description": "",
"hints": [
{
"date": "2016-04-26 22:50:12.6069011 +0430 IRDT",
"description": "narinin"
},
{
"date": "2016-04-26 22:50:12.6069011 +0430 IRDT",
"description": "doros shod"
}
]
}
the script i execute is
hints := hints{}
err := db.C("games").Find(bson.M{"title": game}).Select(bson.M{"hints": 0}).One(&hints)
my 2 structs are
type Game struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Title string `json:"title"`
Imgurl string `json:"imgurl"`
Description string `json:"desc"`
Hints []*hint `bson:"hints", json:"hints"`
}
type hint struct {
Description string `json:"desc"`
Date time.Time `json:"date"`
}
when i use the script all i get is a none sense date string witch is not even in document
how can i get just the slice of hints from a game
You have too keep using Game struct to receive the result, even for only hints column. Also your select query should be .Select(bson.M{"hints": 1}).
I fixed your code, and tried in my local, this one is working.
game := Game{}
err = db.C("games").Find(bson.M{"title": "dota"})
.Select(bson.M{"hints": 1}).One(&game)
if err != nil {
panic(err)
}
for _, hint := range game.Hints {
fmt.Printf("%#v\n", *hint)
}
All properties of game is empty, except Hints.
EDITED 1
To get the first 10 rows on hints, the easiest way would be by playing the slice, but this is bad because it need to fetch all the rows first.
for _, hint := range game.Hints[:10] { // 10 rows
fmt.Printf("%#v\n", *hint)
}
The other solution (which is better), is by using $slice on the .Select() query.
selector := bson.M{"hints": bson.M{"$slice": 10}} // 10 rows
err = db.C("so").Find(bson.M{"title": "dota"})
.Select(selector).One(&game)
EDITED 2
Use []int{skip, limit} on the $slice, to support skip and limit.
selector := bson.M{"hints": bson.M{"$slice": []int{0, 10}}}