Help With Facets

I want to write a Custom DQL mutation function that can apply a facet to an existing edge. In my schema I am experimenting with this syntax as an example.

type Mutation {
	addMaskFacet(cityId: ID!, placeId: ID!): bool @custom(dql: """
	set {
		$cityId <place> $placeId (mask_needed=true) .
	}
	""")
}

I just can’t seem to find the right documentation and this is all very new to me.

Mutations aren’t supported via Custom DQL

Also, check this topic

Given a GraphQL Schema that is seeded with data with a City type that contains a collection of the Place type. Can I do this?

curl -H "Content-Type: application/rdf" http://0.0.0.0:8080/mutate?commitNow=true -XPOST -d $'
{
	set {
		_:city <name> "new york" .
		_:city <dgraph.type> "Place" .
		_:city <name> "Red Carpet Italian Restaurant Downtown:ChIJkWrF6Yi32YgR4vhuu_icbCs" (quality="awesome") .
	}
}'

Then run this query to filter?

curl -H "Content-Type: application/dql" http://0.0.0.0:8080/query -XPOST -d $'
{
  data(func: eq(City.name, "new york")) {
     City.id
     City.name
     City.places {
          Place.name @facets(quality: "awesome")
     }
  }
}'

What am I doing wrong?

The RDF syntax is wrong based on your DQL query.

Pay attention that you have to add the prefix that you are used to in the GraphQL feature. There it is automatically added, here you have to add manually.

This should be your RDF

{
	set {
		_:city <City.name> "new york" .
		_:city <dgraph.type> "City" .
        _:city <City.places> _:Place1 .
    
		_:Place1 <Place.name> "Red Carpet Italian Restaurant Downtown:ChIJkWrF6Yi32YgR4vhuu_icbCs" (quality="awesome") .
		_:Place1 <dgraph.type> "Place" .
	}
}

And this your query

{
  data(func: eq(City.name, "new york")) {
     City.id
     City.name
     City.places {
          Place.name @facets
     }
  }
}

In the facet param, you can have an explicitly value e.g
Place.name @facets(quality) You can also use inequality functions like eq.

THANK YOU!! I have it working now.

I was super close. I was missing this piece in that place:

_:city <City.places> _:Place1 .

This query is working

curl -H "Content-Type: application/dql" http://0.0.0.0:8080/query -XPOST -d $'
{
  data(func: eq(City.name, "new york")) {
     City.id
     City.name
     City.places {
          Place.name @facets(eq(quality, "awesome")) @facets(quality)
     }
  }
}'
1 Like

@MichelDiz, are there any plans for allowing DQL mutations to be extended to mutations?

Yup, custom GraphQL mutations via DQL is planned, covered in the topic that @MichelDiz linked earlier (see also the “Infact” part below):

So I have to wait. There is nothing stopping me from building out a custom HTTP Query that executes DQL against the databse. I will do that for now.

1 Like

@MichelDiz

Given this schema

type City {
	id: ID!
	name: String! @search(by: [hash]) @id
	lat: Float!
	lng: Float!
	places: [Place] @hasInverse(field: city)
	advisory: Advisory @hasInverse(field: city)
	weather: Weather @hasInverse(field: city)
}
type Place {
	id: ID!
	place_id: String! @search(by: [hash]) @id
	name: String! @search(by: [fulltext])
	category: String! @search(by: [exact])
	city: City!
	city_name: String!
	lat: Float!
	lng: Float!
	address: String
	avg_user_rating: Float
	gmaps_url: String
	location_type: [String]
	no_user_rating: Int
	photo_id: String
}

Database is already seeded and Miami has this place in its collection. I run this set to add a facet to the edge between this city and the place.

$ curl -H "Content-Type: application/rdf" http://0.0.0.0:8080/mutate\?commitNow\=true -XPOST -d $'
{
         set {
                _:city <City.name> "miami" .
                _:city <dgraph.type> "City" .
                _:city <City.places> _:Place1 .

                _:Place1 <Place.place_id> "ChIJkWrF6Yi32YgR4vhuu_icbCs" (rating=4) .
                _:Place1 <dgraph.type> "Place" .
        }
}'
{"data":{"code":"Success","message":"Done","queries":null,"uids":{"Place1":"0xc4","city":"0xc5"}},"extensions":{"server_latency":{"parsing_ns":21200,"processing_ns":5477400,"assign_timestamp_ns":1397300,"total_ns":7141700},"txn":{"start_ts":1151,"commit_ts":1152,"preds":["1-\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000City.name","1-\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000City.places","1-\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000Place.place_id","1-\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000dgraph.type"]}}}%

These are not the id’s of those nodes. It looks like a new Place and City node are being created?

{"Place1":"0xc4","city":"0xc5"}}

I want to set a facet to an existing edge between these nodes, not create a new edge. Is that possible?

Yes, as you are using Blank Nodes. Every time you send this mutation it will create a new entity. You have two options. Use the actual UID instead of Blank Nodes, or use upsert mutation instead of pure RDF.

Pure RDF example

{
         set {
                <0x1> <City.name> "Miami" .
                <0x1> <dgraph.type> "City" .
                <0x1> <City.places> <0x3> .

                <0x3> <Place.place_id> "ChIJkWrF6Yi32YgR4vhuu_icbCs" (rating=4) .
                <0x3> <dgraph.type> "Place" .
        }
}

The upsert is a bit different.

The Upsert

upsert {
  query {
    var(func: eq(City.name, "Miami")) {
      v as uid
    }
  }

  mutation {
    set {
      uid(v)  <City.places> <0x3> .
    }
  }
}

Would this work?

set {
		_:city <City.uid> "0x256" .
		_:city <dgraph.type> "City" .
        _:city <City.places> _:Place1 .
    
		_:Place1 <Place.uid> "0x259" (rating=4) .
		_:Place1 <dgraph.type> "Place" .
	}

No, cuz the UID has to follow the RDF syntax. Check this text Mutation Introduction | Intro | Dgraph Tour
The RDF syntax doesn’t have any magic trick as GraphQL does. You have to do everything manually.

BTW, I don’t understand why you have added a rating value to the place name. You should add in the relationship if your intention is to collect ratings.

Facet value holds a single value. But Faced on edge can have infinity facet values. Contextualized with the relation itself.

I’m just playing right now and seeing if I can get this to work. Don’t read into the facet I’m using. It’s just random.

Do I just assume the facet has to be set at the time the edge is being created?

Therefore, I can’t apply this for existing edges?

This didn’t work.

{
        set {
                <0x316> <City.name> "Miami" .
                <0x316> <dgraph.type> "City" .
                <0x316> <City.places> <0x319> .

                <0x319> <Place.place_id> "ChIJkWrF6Yi32YgR4vhuu_icbCs" (rating=4) .
                <0x319> <dgraph.type> "Place" .
        }
}'

This didn’t work

{
        set {
                <0x3d4> <dgraph.type> "City" .
                <0x3d7> <dgraph.type> "Place" .
                <0x3d4> <City.places> <0x3d7> (rating=4) .
        }
}

I might just give up on this.

No, facets can be set and reset all the time. The issue is that every time you send a new mutation with no facet or a new facet, the old one is overwritten.

What is the point of the rating? what do you wanna achieve? Users rating places? So the rating should come from the users, not from the city to the place. Or is this rating something fix? some “global rating”?

The rating is nothing and ignore the practical use of that facet over something else. I just looking to find the syntax needed to make this work.

Given an edge between two existing nodes that were created via GraphQL and a GraphQL Schema, how can I set a facet using DQL?

If you know the UID of both nodes. A simple RDF mutation works.

e.g:

{
         set {
                <0x1> <City.places> <0x3> (rating=4) .
        }
}

In the example above 0x1 is the city, and the 0x3 is the place. This query can be called an “update mutation”. Cuz this relation already exists, you are adding only the facet “rating”.

If you don’t know the UIDs but know how to find them via query. You can use Upsert.

e.g:

upsert {
  query {
    var(func: eq(City.name, "Miami")) {
      v as uid
    }
  }

  mutation {
    set {
      uid(v)  <City.places> <0x3> (rating=4) .
    }
  }
}

“City.places” is the relation (edge). In that edge that you should add the facet. Facets have two scopes, on edge between two nodes, and on the value edge which belongs to that single node. I see that you confuse the scopes. Never give a facet to a value edge if your intention is to add an information about two related nodes.

Ok, I will try this. Thanks for all the time and instruction. This is all new to me.

After working a bit more on this and with @dmai I found a working solution

# This is add a facet between uid(2) and uid(5)
curl -H "Content-Type: application/rdf" http://0.0.0.0:8080/mutate?commitNow=true -XPOST -d $'
{
	set {
                <0x02> <City.places> <0x05> (rating=4) .                
        }
}'

# This does the same thing but with a query first
curl -H "Content-Type: application/rdf" http://0.0.0.0:8080/mutate?commitNow=true -XPOST -d $'
upsert {
  query {
    var(func: eq(City.name, "miami")) {
      c as uid
    }
    var(func: eq(Place.place_id, "ChIJyezI5rC32YgRaD5pUTeSYCI")) {
      p as uid
    }
  }

  mutation {
    set {
      uid(c) <City.places> uid(p) (rating=4) .
    }
  }
}'

# This performs a query to see the facet value and an alias is used for the rating called rating.
curl -s -H "Content-Type: application/dql" http://0.0.0.0:8080/query -XPOST -d $'
{
  data(func: eq(City.name, "miami")) {
     City.id
     City.name
     City.places @facets(eq(rating, 4)) @facets(rating:rating) {
          Place.place_id 
     }
  }
}' | jq

# Here is some of the output
{
  "data": {
    "data": [
      {
        "City.name": "miami",
        "City.places": [
          {
            "Place.place_id": "ChIJT2-gAgC32YgRTTrFGl9JoAo",
            "rating": 4
          },
          {
            "Place.place_id": "ChIJyezI5rC32YgRaD5pUTeSYCI",
            "rating": 4
          }
        ]
      }
    ]
  },
1 Like