<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bila Akpan</title>
    <description>The latest articles on DEV Community by Bila Akpan (@bakpan).</description>
    <link>https://dev.to/bakpan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3782745%2F809dbf7b-c1a5-4ffa-8366-94c0c76acace.png</url>
      <title>DEV Community: Bila Akpan</title>
      <link>https://dev.to/bakpan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bakpan"/>
    <language>en</language>
    <item>
      <title>20 Gremlin Query Examples for Azure Cosmos DB (Practical Cheat Sheet)</title>
      <dc:creator>Bila Akpan</dc:creator>
      <pubDate>Fri, 20 Feb 2026 15:00:32 +0000</pubDate>
      <link>https://dev.to/bakpan/20-gremlin-query-examples-for-azure-cosmos-db-practical-cheat-sheet-51f4</link>
      <guid>https://dev.to/bakpan/20-gremlin-query-examples-for-azure-cosmos-db-practical-cheat-sheet-51f4</guid>
      <description>&lt;h2&gt;
  
  
  Why a Gremlin Cheat Sheet?
&lt;/h2&gt;

&lt;p&gt;The Gremlin query language is powerful but has a steep learning curve — especially on Azure Cosmos DB, where partition keys, RU costs, and unsupported steps add complexity that standard TinkerPop tutorials don't cover. This post gives you 20 copy-paste-ready queries organized by category, with Cosmos DB-specific tips for each one.&lt;/p&gt;

&lt;p&gt;All examples use an e-commerce graph with &lt;strong&gt;product&lt;/strong&gt;, &lt;strong&gt;person&lt;/strong&gt;, and &lt;strong&gt;category&lt;/strong&gt; vertices, but the patterns apply to any domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Add a Vertex with Partition Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.addV('product').property('id', 'prod-001').property('category', 'electronics').property('name', 'Wireless Headphones').property('price', 79.99).property('inStock', true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every vertex needs an &lt;code&gt;id&lt;/code&gt; (unique within the partition) and the partition key property. If your container's partition key is &lt;code&gt;/category&lt;/code&gt;, you must include it as a property. Without it, Cosmos DB assigns a random partition and your queries become expensive cross-partition fan-outs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add an Edge
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V(['electronics', 'prod-001']).addE('purchased_by').to(g.V(['customers', 'user-042'])).property('date', '2025-01-15').property('quantity', 2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tuple syntax &lt;code&gt;['partitionKeyValue', 'id']&lt;/code&gt; is a Cosmos DB feature for efficient point reads. Edges are stored with their source vertex's partition, so &lt;code&gt;out()&lt;/code&gt; traversals are fast (single partition) while &lt;code&gt;in()&lt;/code&gt; traversals are always cross-partition.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Upsert a Vertex (Create or Update)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('prod-001').has('category', 'electronics').fold().coalesce(unfold(), addV('product').property('id', 'prod-001').property('category', 'electronics')).property('name', 'Wireless Headphones Pro').property('price', 89.99)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;fold()&lt;/code&gt; / &lt;code&gt;coalesce()&lt;/code&gt; / &lt;code&gt;unfold()&lt;/code&gt; pattern is the standard Gremlin upsert. If the vertex exists, &lt;code&gt;unfold()&lt;/code&gt; returns it. If not, &lt;code&gt;addV()&lt;/code&gt; creates it. Properties are then set either way. This pattern is critical for idempotent data pipelines — Microsoft's own docs don't cover it well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading and Filtering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4. Filter by Property Value
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('product').has('category', 'electronics').has('price', gt(50)).valueMap('name', 'price')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always include the partition key in your filter (here &lt;code&gt;category&lt;/code&gt;) to avoid cross-partition queries. Use &lt;code&gt;valueMap()&lt;/code&gt; instead of &lt;code&gt;values()&lt;/code&gt; when you need key-value pairs, not just values.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. String Filtering with TextP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('product').has('category', 'electronics').has('name', TextP.containing('Wireless')).valueMap('name', 'price')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;TextP.containing()&lt;/code&gt; does substring matching. Also available: &lt;code&gt;startingWith()&lt;/code&gt;, &lt;code&gt;endingWith()&lt;/code&gt;, &lt;code&gt;notContaining()&lt;/code&gt;. These are supported on Cosmos DB but not well-documented.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Range Queries and Sorting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('product').has('category', 'electronics').has('price', between(50, 150)).order().by('price', asc).valueMap('name', 'price')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;between()&lt;/code&gt; predicate is shorthand for &lt;code&gt;gte(50).and(lt(150))&lt;/code&gt;. Other predicates: &lt;code&gt;gt&lt;/code&gt;, &lt;code&gt;gte&lt;/code&gt;, &lt;code&gt;lt&lt;/code&gt;, &lt;code&gt;lte&lt;/code&gt;, &lt;code&gt;eq&lt;/code&gt;, &lt;code&gt;neq&lt;/code&gt;. Always combine with &lt;code&gt;limit()&lt;/code&gt; or &lt;code&gt;range()&lt;/code&gt; to control RU cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Pagination with Range
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('product').has('category', 'electronics').order().by('name', asc).range(0, 10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;range(start, end)&lt;/code&gt; provides offset-based pagination. &lt;code&gt;range(0, 10)&lt;/code&gt; returns the first 10 results, &lt;code&gt;range(10, 20)&lt;/code&gt; returns the next 10. More RU-efficient than fetching everything and slicing client-side. Note: &lt;code&gt;limit(n)&lt;/code&gt; is equivalent to &lt;code&gt;range(0, n)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traversing Relationships
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. Find Direct Neighbors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('alice').out('knows').values('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;out()&lt;/code&gt; follows outgoing edges, &lt;code&gt;in()&lt;/code&gt; follows incoming, &lt;code&gt;both()&lt;/code&gt; follows both. On Cosmos DB, prefer &lt;code&gt;out()&lt;/code&gt; over &lt;code&gt;in()&lt;/code&gt; — outgoing traversals stay within the source vertex's partition, while incoming traversals fan out across all partitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Two-Hop Traversal with Dedup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('alice').out('knows').out('knows').dedup().values('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chain &lt;code&gt;out()&lt;/code&gt; for multi-hop traversals. Without &lt;code&gt;dedup()&lt;/code&gt;, the same vertex can appear multiple times through different paths — and the result set grows exponentially with each hop.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Get Edges with Properties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('alice').outE('knows').as('e').inV().as('v').select('e', 'v').by('since').by('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;outE()&lt;/code&gt; to access edge objects directly. The &lt;code&gt;as()&lt;/code&gt; / &lt;code&gt;select()&lt;/code&gt; / &lt;code&gt;by()&lt;/code&gt; pattern projects properties from both edges and vertices into a clean result.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Find Common Connections
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('alice').out('knows').where(__.in('knows').has('id', 'bob')).values('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finds mutual friends — people both Alice and Bob know. The &lt;code&gt;where()&lt;/code&gt; step applies a nested traversal as a filter. The &lt;code&gt;__&lt;/code&gt; (double underscore) starts an anonymous traversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating and Deleting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  12. Update a Property
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('prod-001').has('category', 'electronics').property('price', 69.99)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting a property that already exists overwrites it. Always include the partition key in the lookup to avoid a cross-partition read before the write.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Drop a Vertex
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('prod-001').has('category', 'electronics').drop()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;drop()&lt;/code&gt; removes the vertex and all connected edges. Never run &lt;code&gt;g.V().drop()&lt;/code&gt; in production — it scans the entire graph and can consume thousands of RUs. Instead, batch deletes with &lt;code&gt;g.V().hasLabel('product').limit(100).drop()&lt;/code&gt; in a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  14. Shortest Path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('alice').repeat(both('knows').simplePath()).until(has('id', 'eve')).path().limit(1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;repeat()&lt;/code&gt; / &lt;code&gt;until()&lt;/code&gt; does a breadth-first search (Cosmos DB always uses breadth-first, unlike standard TinkerPop which defaults to depth-first). &lt;code&gt;simplePath()&lt;/code&gt; prevents cycles. Because of breadth-first traversal, &lt;code&gt;limit(1)&lt;/code&gt; guarantees the shortest path.&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Cycle Detection (Fraud Pattern)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('acct-001').as('start').repeat(out('transferred_to')).times(5).where(eq('start')).path().by('accountId').dedup().limit(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detects circular money transfer patterns up to 5 hops — a classic fraud detection query. The &lt;code&gt;where(eq('start'))&lt;/code&gt; check confirms the traversal returned to the origin, proving a cycle exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  16. Recommendation Engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('prod-001').has('category', 'electronics').in('purchased').out('purchased').where(neq('a')).as('a').groupCount().by('name').order(local).by(values, desc).limit(local, 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Collaborative filtering: start from a product, find customers who bought it, find what else they bought, rank by frequency. The top 5 results are your "Customers who bought this also bought" recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. Variable-Depth Hierarchy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V('dept-engineering').repeat(out('manages')).until(outE('manages').count().is(0)).emit().path().by('name')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recursively traverses a management tree until leaf nodes. &lt;code&gt;emit()&lt;/code&gt; outputs vertices at every level, not just the leaves. The &lt;code&gt;path().by('name')&lt;/code&gt; shows the full chain from root to each descendant.&lt;/p&gt;

&lt;h3&gt;
  
  
  18. Group and Count by Label
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().groupCount().by(label)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns a map of vertex label to count — a quick schema summary. This is one of the first queries to run on any new graph. Note: this is always a cross-partition query.&lt;/p&gt;

&lt;h3&gt;
  
  
  19. Find Most Connected Vertices
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('person').project('name', 'connections').by('name').by(both().count()).order().by(select('connections'), desc).limit(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identifies hub nodes — the top 10 most-connected people. Useful for finding influencers in social networks or critical dependencies in infrastructure graphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  20. Diagnose Query Cost with executionProfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g.V().hasLabel('product').has('category', 'electronics').has('price', gt(50)).out('reviewed_by').valueMap('name', 'rating').executionProfile()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append &lt;code&gt;executionProfile()&lt;/code&gt; to any query to see detailed metrics: RU charge, time per step, and index utilization. This is the single most important tool for optimizing Cosmos DB Gremlin queries — use it to find which step is consuming the most RUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cosmos DB Gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partition key in every lookup&lt;/strong&gt;: Always include &lt;code&gt;has('partitionKey', 'value')&lt;/code&gt; or use tuple syntax &lt;code&gt;g.V(['pkValue', 'id'])&lt;/code&gt;. Without it, every lookup is a cross-partition fan-out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;in()&lt;/code&gt; is expensive&lt;/strong&gt;: Edges are stored with the source vertex. &lt;code&gt;out()&lt;/code&gt; stays in one partition; &lt;code&gt;in()&lt;/code&gt; fans out across all partitions. If you need fast &lt;code&gt;in()&lt;/code&gt; traversals, add reverse edges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-traversal &lt;code&gt;.V()&lt;/code&gt; skips the index&lt;/strong&gt;: Only the first &lt;code&gt;.V()&lt;/code&gt; in a query uses the index. Subsequent &lt;code&gt;.V()&lt;/code&gt; calls do full scans. Restructure with &lt;code&gt;.map()&lt;/code&gt; or &lt;code&gt;.union()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lambdas&lt;/strong&gt;: &lt;code&gt;map { it.get() }&lt;/code&gt; and &lt;code&gt;.by { it.value('x') }&lt;/code&gt; are not supported. Use standard steps like &lt;code&gt;.map(__.values('x'))&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;match()&lt;/code&gt; step&lt;/strong&gt;: Declarative pattern matching is not available on Cosmos DB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties must be primitives&lt;/strong&gt;: No nested objects. Flatten complex data into key-value pairs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphSON v2 only&lt;/strong&gt;: Use &lt;code&gt;GraphSON2MessageSerializer&lt;/code&gt;, not v3. Cosmos DB returns non-standard untyped GraphSON (known issue TINKERPOP-2581).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch large deletes&lt;/strong&gt;: &lt;code&gt;g.V().drop()&lt;/code&gt; on a large graph can time out or consume massive RUs. Delete in batches of 100-500 with &lt;code&gt;limit()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built &lt;a href="https://gremlinstudio.net" rel="noopener noreferrer"&gt;GremlinStudio&lt;/a&gt; to make working with Cosmos DB Gremlin queries more productive — it has a Monaco editor with Gremlin autocomplete, interactive graph visualization, a step-by-step debugger, and automatic RU cost display. Free 7-day trial, no credit card required.&lt;/p&gt;

</description>
      <category>cosmosdb</category>
      <category>database</category>
      <category>azure</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
